Merge "Add attestation API to Android KeyStore." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index ea6a679..b9f932b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -104,6 +104,7 @@
field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
+ field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS";
field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
@@ -197,7 +198,6 @@
public static final class R.attr {
ctor public R.attr();
- field public static final int abiOverride = 16844054; // 0x1010516
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -1364,6 +1364,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
+ field public static final int use32bitAbi = 16844054; // 0x1010516
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -22997,7 +22998,7 @@
method public void notifyRecordingStopped(android.net.Uri);
method public abstract void onConnect(android.net.Uri);
method public abstract void onDisconnect();
- method public abstract void onStartRecording();
+ method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
}
@@ -23043,7 +23044,7 @@
ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
method public void connect(java.lang.String, android.net.Uri);
method public void disconnect();
- method public void startRecording();
+ method public void startRecording(android.net.Uri);
method public void stopRecording();
}
@@ -40662,6 +40663,21 @@
method public static android.view.FocusFinder getInstance();
}
+ public final class FrameMetrics {
+ ctor public FrameMetrics(android.view.FrameMetrics);
+ method public long getMetric(int);
+ field public static final int ANIMATION_DURATION = 2; // 0x2
+ field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+ field public static final int DRAW_DURATION = 4; // 0x4
+ field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+ field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+ field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
+ field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
+ field public static final int SYNC_DURATION = 5; // 0x5
+ field public static final int TOTAL_DURATION = 8; // 0x8
+ field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+ }
+
public abstract class FrameStats {
ctor public FrameStats();
method public final long getEndTimeNano();
@@ -43165,6 +43181,7 @@
ctor public Window(android.content.Context);
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void addFlags(int);
+ method public final void addFrameMetricsListener(android.view.Window.FrameMetricsListener, android.os.Handler);
method public void clearFlags(int);
method public abstract void closeAllPanels();
method public abstract void closePanel(int);
@@ -43216,6 +43233,7 @@
method public abstract boolean performContextMenuIdentifierAction(int, int);
method public abstract boolean performPanelIdentifierAction(int, int, int);
method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+ method public final void removeFrameMetricsListener(android.view.Window.FrameMetricsListener);
method public boolean requestFeature(int);
method public abstract void restoreHierarchyState(android.os.Bundle);
method public abstract android.os.Bundle saveHierarchyState();
@@ -43341,6 +43359,10 @@
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static abstract interface Window.FrameMetricsListener {
+ method public abstract void onMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
+ }
+
public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
}
@@ -58017,8 +58039,11 @@
method public static boolean equals(java.lang.Object, java.lang.Object);
method public static int hash(java.lang.Object...);
method public static int hashCode(java.lang.Object);
+ method public static boolean isNull(java.lang.Object);
+ method public static boolean nonNull(java.lang.Object);
method public static T requireNonNull(T);
method public static T requireNonNull(T, java.lang.String);
+ method public static T requireNonNull(T, java.util.function.Supplier<java.lang.String>);
method public static java.lang.String toString(java.lang.Object);
method public static java.lang.String toString(java.lang.Object, java.lang.String);
}
@@ -59723,6 +59748,217 @@
}
+package java.util.function {
+
+ public abstract interface BiConsumer {
+ method public abstract void accept(T, U);
+ method public default java.util.function.BiConsumer<T, U> andThen(java.util.function.BiConsumer<? super T, ? super U>);
+ }
+
+ public abstract interface BiFunction {
+ method public default java.util.function.BiFunction<T, U, V> andThen(java.util.function.Function<? super R, ? extends V>);
+ method public abstract R apply(T, U);
+ }
+
+ public abstract interface BiPredicate {
+ method public default java.util.function.BiPredicate<T, U> and(java.util.function.BiPredicate<? super T, ? super U>);
+ method public default java.util.function.BiPredicate<T, U> negate();
+ method public default java.util.function.BiPredicate<T, U> or(java.util.function.BiPredicate<? super T, ? super U>);
+ method public abstract boolean test(T, U);
+ }
+
+ public abstract interface BinaryOperator implements java.util.function.BiFunction {
+ method public static java.util.function.BinaryOperator<T> maxBy(java.util.Comparator<? super T>);
+ method public static java.util.function.BinaryOperator<T> minBy(java.util.Comparator<? super T>);
+ }
+
+ public abstract interface BooleanSupplier {
+ method public abstract boolean getAsBoolean();
+ }
+
+ public abstract interface Consumer {
+ method public abstract void accept(T);
+ method public default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T>);
+ }
+
+ public abstract interface DoubleBinaryOperator {
+ method public abstract double applyAsDouble(double, double);
+ }
+
+ public abstract interface DoubleConsumer {
+ method public abstract void accept(double);
+ method public default java.util.function.DoubleConsumer andThen(java.util.function.DoubleConsumer);
+ }
+
+ public abstract interface DoubleFunction {
+ method public abstract R apply(double);
+ }
+
+ public abstract interface DoublePredicate {
+ method public default java.util.function.DoublePredicate and(java.util.function.DoublePredicate);
+ method public default java.util.function.DoublePredicate negate();
+ method public default java.util.function.DoublePredicate or(java.util.function.DoublePredicate);
+ method public abstract boolean test(double);
+ }
+
+ public abstract interface DoubleSupplier {
+ method public abstract double getAsDouble();
+ }
+
+ public abstract interface DoubleToIntFunction {
+ method public abstract int applyAsInt(double);
+ }
+
+ public abstract interface DoubleToLongFunction {
+ method public abstract long applyAsLong(double);
+ }
+
+ public abstract interface DoubleUnaryOperator {
+ method public default java.util.function.DoubleUnaryOperator andThen(java.util.function.DoubleUnaryOperator);
+ method public abstract double applyAsDouble(double);
+ method public default java.util.function.DoubleUnaryOperator compose(java.util.function.DoubleUnaryOperator);
+ method public static java.util.function.DoubleUnaryOperator identity();
+ }
+
+ public abstract interface Function {
+ method public default java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V>);
+ method public abstract R apply(T);
+ method public default java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T>);
+ method public static java.util.function.Function<T, T> identity();
+ }
+
+ public abstract interface IntBinaryOperator {
+ method public abstract int applyAsInt(int, int);
+ }
+
+ public abstract interface IntConsumer {
+ method public abstract void accept(int);
+ method public default java.util.function.IntConsumer andThen(java.util.function.IntConsumer);
+ }
+
+ public abstract interface IntFunction {
+ method public abstract R apply(int);
+ }
+
+ public abstract interface IntPredicate {
+ method public default java.util.function.IntPredicate and(java.util.function.IntPredicate);
+ method public default java.util.function.IntPredicate negate();
+ method public default java.util.function.IntPredicate or(java.util.function.IntPredicate);
+ method public abstract boolean test(int);
+ }
+
+ public abstract interface IntSupplier {
+ method public abstract int getAsInt();
+ }
+
+ public abstract interface IntToDoubleFunction {
+ method public abstract double applyAsDouble(int);
+ }
+
+ public abstract interface IntToLongFunction {
+ method public abstract long applyAsLong(int);
+ }
+
+ public abstract interface IntUnaryOperator {
+ method public default java.util.function.IntUnaryOperator andThen(java.util.function.IntUnaryOperator);
+ method public abstract int applyAsInt(int);
+ method public default java.util.function.IntUnaryOperator compose(java.util.function.IntUnaryOperator);
+ method public static java.util.function.IntUnaryOperator identity();
+ }
+
+ public abstract interface LongBinaryOperator {
+ method public abstract long applyAsLong(long, long);
+ }
+
+ public abstract interface LongConsumer {
+ method public abstract void accept(long);
+ method public default java.util.function.LongConsumer andThen(java.util.function.LongConsumer);
+ }
+
+ public abstract interface LongFunction {
+ method public abstract R apply(long);
+ }
+
+ public abstract interface LongPredicate {
+ method public default java.util.function.LongPredicate and(java.util.function.LongPredicate);
+ method public default java.util.function.LongPredicate negate();
+ method public default java.util.function.LongPredicate or(java.util.function.LongPredicate);
+ method public abstract boolean test(long);
+ }
+
+ public abstract interface LongSupplier {
+ method public abstract long getAsLong();
+ }
+
+ public abstract interface LongToDoubleFunction {
+ method public abstract double applyAsDouble(long);
+ }
+
+ public abstract interface LongToIntFunction {
+ method public abstract int applyAsInt(long);
+ }
+
+ public abstract interface LongUnaryOperator {
+ method public default java.util.function.LongUnaryOperator andThen(java.util.function.LongUnaryOperator);
+ method public abstract long applyAsLong(long);
+ method public default java.util.function.LongUnaryOperator compose(java.util.function.LongUnaryOperator);
+ method public static java.util.function.LongUnaryOperator identity();
+ }
+
+ public abstract interface ObjDoubleConsumer {
+ method public abstract void accept(T, double);
+ }
+
+ public abstract interface ObjIntConsumer {
+ method public abstract void accept(T, int);
+ }
+
+ public abstract interface ObjLongConsumer {
+ method public abstract void accept(T, long);
+ }
+
+ public abstract interface Predicate {
+ method public default java.util.function.Predicate<T> and(java.util.function.Predicate<? super T>);
+ method public static java.util.function.Predicate<T> isEqual(java.lang.Object);
+ method public default java.util.function.Predicate<T> negate();
+ method public default java.util.function.Predicate<T> or(java.util.function.Predicate<? super T>);
+ method public abstract boolean test(T);
+ }
+
+ public abstract interface Supplier {
+ method public abstract T get();
+ }
+
+ public abstract interface ToDoubleBiFunction {
+ method public abstract double applyAsDouble(T, U);
+ }
+
+ public abstract interface ToDoubleFunction {
+ method public abstract double applyAsDouble(T);
+ }
+
+ public abstract interface ToIntBiFunction {
+ method public abstract int applyAsInt(T, U);
+ }
+
+ public abstract interface ToIntFunction {
+ method public abstract int applyAsInt(T);
+ }
+
+ public abstract interface ToLongBiFunction {
+ method public abstract long applyAsLong(T, U);
+ }
+
+ public abstract interface ToLongFunction {
+ method public abstract long applyAsLong(T);
+ }
+
+ public abstract interface UnaryOperator implements java.util.function.Function {
+ method public static java.util.function.UnaryOperator<T> identity();
+ }
+
+}
+
package java.util.jar {
public class Attributes implements java.lang.Cloneable java.util.Map {
diff --git a/api/system-current.txt b/api/system-current.txt
index 0288a49..b404bb0 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -293,7 +293,6 @@
public static final class R.attr {
ctor public R.attr();
- field public static final int abiOverride = 16844054; // 0x1010516
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -1464,6 +1463,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
+ field public static final int use32bitAbi = 16844054; // 0x1010516
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -21082,6 +21082,7 @@
public final class AudioAttributes implements android.os.Parcelable {
method public int describeContents();
method public int getAllFlags();
+ method public android.os.Bundle getBundle();
method public int getCapturePreset();
method public int getContentType();
method public int getFlags();
@@ -21120,6 +21121,7 @@
public static class AudioAttributes.Builder {
ctor public AudioAttributes.Builder();
ctor public AudioAttributes.Builder(android.media.AudioAttributes);
+ method public android.media.AudioAttributes.Builder addBundle(android.os.Bundle);
method public android.media.AudioAttributes build();
method public android.media.AudioAttributes.Builder setCapturePreset(int);
method public android.media.AudioAttributes.Builder setContentType(int);
@@ -24748,7 +24750,7 @@
method public abstract void onConnect(android.net.Uri);
method public void onConnect(android.net.Uri, android.os.Bundle);
method public abstract void onDisconnect();
- method public abstract void onStartRecording();
+ method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
}
@@ -24800,7 +24802,7 @@
method public void connect(java.lang.String, android.net.Uri, android.os.Bundle);
method public void disconnect();
method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle);
- method public void startRecording();
+ method public void startRecording(android.net.Uri);
method public void stopRecording();
}
@@ -26233,12 +26235,18 @@
}
public class RttManager {
+ method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
+ method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities();
method public android.net.wifi.RttManager.RttCapabilities getRttCapabilities();
method public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener);
method public void stopRanging(android.net.wifi.RttManager.RttListener);
field public static final int BASE = 160256; // 0x27200
field public static final int CMD_OP_ABORTED = 160260; // 0x27204
+ field public static final int CMD_OP_DISABLE_RESPONDER = 160262; // 0x27206
+ field public static final int CMD_OP_ENABLE_RESPONDER = 160261; // 0x27205
+ field public static final int CMD_OP_ENALBE_RESPONDER_FAILED = 160264; // 0x27208
+ field public static final int CMD_OP_ENALBE_RESPONDER_SUCCEEDED = 160263; // 0x27207
field public static final int CMD_OP_FAILED = 160258; // 0x27202
field public static final int CMD_OP_START_RANGING = 160256; // 0x27200
field public static final int CMD_OP_STOP_RANGING = 160257; // 0x27201
@@ -26247,6 +26255,7 @@
field public static final int PREAMBLE_HT = 2; // 0x2
field public static final int PREAMBLE_LEGACY = 1; // 0x1
field public static final int PREAMBLE_VHT = 4; // 0x4
+ field public static final int REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; // 0xfffffffa
field public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd
field public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc
field public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe
@@ -26314,6 +26323,25 @@
field public android.net.wifi.RttManager.RttResult[] mResults;
}
+ public static abstract class RttManager.ResponderCallback {
+ ctor public RttManager.ResponderCallback();
+ method public abstract void onResponderEnableFailure(int);
+ method public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig);
+ }
+
+ public static class RttManager.ResponderConfig implements android.os.Parcelable {
+ ctor public RttManager.ResponderConfig();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.net.wifi.RttManager.ResponderConfig> CREATOR;
+ field public int centerFreq0;
+ field public int centerFreq1;
+ field public int channelWidth;
+ field public int frequency;
+ field public java.lang.String macAddress;
+ field public int preamble;
+ }
+
public static class RttManager.RttCapabilities implements android.os.Parcelable {
ctor public RttManager.RttCapabilities();
method public int describeContents();
@@ -26323,6 +26351,7 @@
field public boolean lcrSupported;
field public boolean oneSidedRttSupported;
field public int preambleSupported;
+ field public boolean responderSupported;
field public deprecated boolean supportedPeerType;
field public deprecated boolean supportedType;
field public boolean twoSided11McRttSupported;
@@ -31502,6 +31531,51 @@
ctor public TransactionTooLargeException(java.lang.String);
}
+ public class UpdateEngine {
+ ctor public UpdateEngine();
+ method public void applyPayload(java.lang.String, long, long, java.lang.String[]) throws android.os.RemoteException;
+ method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler) throws android.os.RemoteException;
+ method public boolean bind(android.os.UpdateEngineCallback) throws android.os.RemoteException;
+ method public void cancel() throws android.os.RemoteException;
+ method public void resume() throws android.os.RemoteException;
+ method public void suspend() throws android.os.RemoteException;
+ }
+
+ public static final class UpdateEngine.ErrorCodeConstants {
+ ctor public UpdateEngine.ErrorCodeConstants();
+ field public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; // 0xc
+ field public static final int DOWNLOAD_TRANSFER_ERROR = 9; // 0x9
+ field public static final int ERROR = 1; // 0x1
+ field public static final int FILESYSTEM_COPIER_ERROR = 4; // 0x4
+ field public static final int INSTALL_DEVICE_OPEN_ERROR = 7; // 0x7
+ field public static final int KERNEL_DEVICE_OPEN_ERROR = 8; // 0x8
+ field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
+ field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
+ field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
+ field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5
+ field public static final int SUCCESS = 0; // 0x0
+ }
+
+ public static final class UpdateEngine.UpdateStatusConstants {
+ ctor public UpdateEngine.UpdateStatusConstants();
+ field public static final int ATTEMPTING_ROLLBACK = 8; // 0x8
+ field public static final int CHECKING_FOR_UPDATE = 1; // 0x1
+ field public static final int DISABLED = 9; // 0x9
+ field public static final int DOWNLOADING = 3; // 0x3
+ field public static final int FINALIZING = 5; // 0x5
+ field public static final int IDLE = 0; // 0x0
+ field public static final int REPORTING_ERROR_EVENT = 7; // 0x7
+ field public static final int UPDATED_NEED_REBOOT = 6; // 0x6
+ field public static final int UPDATE_AVAILABLE = 2; // 0x2
+ field public static final int VERIFYING = 4; // 0x4
+ }
+
+ public abstract class UpdateEngineCallback {
+ ctor public UpdateEngineCallback();
+ method public abstract void onPayloadApplicationComplete(int);
+ method public abstract void onStatusUpdate(int, float);
+ }
+
public final class UserHandle implements android.os.Parcelable {
ctor public UserHandle(android.os.Parcel);
method public int describeContents();
@@ -43341,6 +43415,21 @@
method public static android.view.FocusFinder getInstance();
}
+ public final class FrameMetrics {
+ ctor public FrameMetrics(android.view.FrameMetrics);
+ method public long getMetric(int);
+ field public static final int ANIMATION_DURATION = 2; // 0x2
+ field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+ field public static final int DRAW_DURATION = 4; // 0x4
+ field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+ field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+ field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
+ field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
+ field public static final int SYNC_DURATION = 5; // 0x5
+ field public static final int TOTAL_DURATION = 8; // 0x8
+ field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+ }
+
public abstract class FrameStats {
ctor public FrameStats();
method public final long getEndTimeNano();
@@ -45844,6 +45933,7 @@
ctor public Window(android.content.Context);
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void addFlags(int);
+ method public final void addFrameMetricsListener(android.view.Window.FrameMetricsListener, android.os.Handler);
method public void clearFlags(int);
method public abstract void closeAllPanels();
method public abstract void closePanel(int);
@@ -45895,6 +45985,7 @@
method public abstract boolean performContextMenuIdentifierAction(int, int);
method public abstract boolean performPanelIdentifierAction(int, int, int);
method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+ method public final void removeFrameMetricsListener(android.view.Window.FrameMetricsListener);
method public boolean requestFeature(int);
method public abstract void restoreHierarchyState(android.os.Bundle);
method public abstract android.os.Bundle saveHierarchyState();
@@ -46021,6 +46112,10 @@
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static abstract interface Window.FrameMetricsListener {
+ method public abstract void onMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
+ }
+
public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
}
@@ -61032,8 +61127,11 @@
method public static boolean equals(java.lang.Object, java.lang.Object);
method public static int hash(java.lang.Object...);
method public static int hashCode(java.lang.Object);
+ method public static boolean isNull(java.lang.Object);
+ method public static boolean nonNull(java.lang.Object);
method public static T requireNonNull(T);
method public static T requireNonNull(T, java.lang.String);
+ method public static T requireNonNull(T, java.util.function.Supplier<java.lang.String>);
method public static java.lang.String toString(java.lang.Object);
method public static java.lang.String toString(java.lang.Object, java.lang.String);
}
@@ -62738,6 +62836,217 @@
}
+package java.util.function {
+
+ public abstract interface BiConsumer {
+ method public abstract void accept(T, U);
+ method public default java.util.function.BiConsumer<T, U> andThen(java.util.function.BiConsumer<? super T, ? super U>);
+ }
+
+ public abstract interface BiFunction {
+ method public default java.util.function.BiFunction<T, U, V> andThen(java.util.function.Function<? super R, ? extends V>);
+ method public abstract R apply(T, U);
+ }
+
+ public abstract interface BiPredicate {
+ method public default java.util.function.BiPredicate<T, U> and(java.util.function.BiPredicate<? super T, ? super U>);
+ method public default java.util.function.BiPredicate<T, U> negate();
+ method public default java.util.function.BiPredicate<T, U> or(java.util.function.BiPredicate<? super T, ? super U>);
+ method public abstract boolean test(T, U);
+ }
+
+ public abstract interface BinaryOperator implements java.util.function.BiFunction {
+ method public static java.util.function.BinaryOperator<T> maxBy(java.util.Comparator<? super T>);
+ method public static java.util.function.BinaryOperator<T> minBy(java.util.Comparator<? super T>);
+ }
+
+ public abstract interface BooleanSupplier {
+ method public abstract boolean getAsBoolean();
+ }
+
+ public abstract interface Consumer {
+ method public abstract void accept(T);
+ method public default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T>);
+ }
+
+ public abstract interface DoubleBinaryOperator {
+ method public abstract double applyAsDouble(double, double);
+ }
+
+ public abstract interface DoubleConsumer {
+ method public abstract void accept(double);
+ method public default java.util.function.DoubleConsumer andThen(java.util.function.DoubleConsumer);
+ }
+
+ public abstract interface DoubleFunction {
+ method public abstract R apply(double);
+ }
+
+ public abstract interface DoublePredicate {
+ method public default java.util.function.DoublePredicate and(java.util.function.DoublePredicate);
+ method public default java.util.function.DoublePredicate negate();
+ method public default java.util.function.DoublePredicate or(java.util.function.DoublePredicate);
+ method public abstract boolean test(double);
+ }
+
+ public abstract interface DoubleSupplier {
+ method public abstract double getAsDouble();
+ }
+
+ public abstract interface DoubleToIntFunction {
+ method public abstract int applyAsInt(double);
+ }
+
+ public abstract interface DoubleToLongFunction {
+ method public abstract long applyAsLong(double);
+ }
+
+ public abstract interface DoubleUnaryOperator {
+ method public default java.util.function.DoubleUnaryOperator andThen(java.util.function.DoubleUnaryOperator);
+ method public abstract double applyAsDouble(double);
+ method public default java.util.function.DoubleUnaryOperator compose(java.util.function.DoubleUnaryOperator);
+ method public static java.util.function.DoubleUnaryOperator identity();
+ }
+
+ public abstract interface Function {
+ method public default java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V>);
+ method public abstract R apply(T);
+ method public default java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T>);
+ method public static java.util.function.Function<T, T> identity();
+ }
+
+ public abstract interface IntBinaryOperator {
+ method public abstract int applyAsInt(int, int);
+ }
+
+ public abstract interface IntConsumer {
+ method public abstract void accept(int);
+ method public default java.util.function.IntConsumer andThen(java.util.function.IntConsumer);
+ }
+
+ public abstract interface IntFunction {
+ method public abstract R apply(int);
+ }
+
+ public abstract interface IntPredicate {
+ method public default java.util.function.IntPredicate and(java.util.function.IntPredicate);
+ method public default java.util.function.IntPredicate negate();
+ method public default java.util.function.IntPredicate or(java.util.function.IntPredicate);
+ method public abstract boolean test(int);
+ }
+
+ public abstract interface IntSupplier {
+ method public abstract int getAsInt();
+ }
+
+ public abstract interface IntToDoubleFunction {
+ method public abstract double applyAsDouble(int);
+ }
+
+ public abstract interface IntToLongFunction {
+ method public abstract long applyAsLong(int);
+ }
+
+ public abstract interface IntUnaryOperator {
+ method public default java.util.function.IntUnaryOperator andThen(java.util.function.IntUnaryOperator);
+ method public abstract int applyAsInt(int);
+ method public default java.util.function.IntUnaryOperator compose(java.util.function.IntUnaryOperator);
+ method public static java.util.function.IntUnaryOperator identity();
+ }
+
+ public abstract interface LongBinaryOperator {
+ method public abstract long applyAsLong(long, long);
+ }
+
+ public abstract interface LongConsumer {
+ method public abstract void accept(long);
+ method public default java.util.function.LongConsumer andThen(java.util.function.LongConsumer);
+ }
+
+ public abstract interface LongFunction {
+ method public abstract R apply(long);
+ }
+
+ public abstract interface LongPredicate {
+ method public default java.util.function.LongPredicate and(java.util.function.LongPredicate);
+ method public default java.util.function.LongPredicate negate();
+ method public default java.util.function.LongPredicate or(java.util.function.LongPredicate);
+ method public abstract boolean test(long);
+ }
+
+ public abstract interface LongSupplier {
+ method public abstract long getAsLong();
+ }
+
+ public abstract interface LongToDoubleFunction {
+ method public abstract double applyAsDouble(long);
+ }
+
+ public abstract interface LongToIntFunction {
+ method public abstract int applyAsInt(long);
+ }
+
+ public abstract interface LongUnaryOperator {
+ method public default java.util.function.LongUnaryOperator andThen(java.util.function.LongUnaryOperator);
+ method public abstract long applyAsLong(long);
+ method public default java.util.function.LongUnaryOperator compose(java.util.function.LongUnaryOperator);
+ method public static java.util.function.LongUnaryOperator identity();
+ }
+
+ public abstract interface ObjDoubleConsumer {
+ method public abstract void accept(T, double);
+ }
+
+ public abstract interface ObjIntConsumer {
+ method public abstract void accept(T, int);
+ }
+
+ public abstract interface ObjLongConsumer {
+ method public abstract void accept(T, long);
+ }
+
+ public abstract interface Predicate {
+ method public default java.util.function.Predicate<T> and(java.util.function.Predicate<? super T>);
+ method public static java.util.function.Predicate<T> isEqual(java.lang.Object);
+ method public default java.util.function.Predicate<T> negate();
+ method public default java.util.function.Predicate<T> or(java.util.function.Predicate<? super T>);
+ method public abstract boolean test(T);
+ }
+
+ public abstract interface Supplier {
+ method public abstract T get();
+ }
+
+ public abstract interface ToDoubleBiFunction {
+ method public abstract double applyAsDouble(T, U);
+ }
+
+ public abstract interface ToDoubleFunction {
+ method public abstract double applyAsDouble(T);
+ }
+
+ public abstract interface ToIntBiFunction {
+ method public abstract int applyAsInt(T, U);
+ }
+
+ public abstract interface ToIntFunction {
+ method public abstract int applyAsInt(T);
+ }
+
+ public abstract interface ToLongBiFunction {
+ method public abstract long applyAsLong(T, U);
+ }
+
+ public abstract interface ToLongFunction {
+ method public abstract long applyAsLong(T);
+ }
+
+ public abstract interface UnaryOperator implements java.util.function.Function {
+ method public static java.util.function.UnaryOperator<T> identity();
+ }
+
+}
+
package java.util.jar {
public class Attributes implements java.lang.Cloneable java.util.Map {
diff --git a/api/test-current.txt b/api/test-current.txt
index 5fb0b52..9f8b8a8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -104,6 +104,7 @@
field public static final java.lang.String READ_WRITE_CONTACT_METADATA = "android.permission.READ_WRITE_CONTACT_METADATA";
field public static final java.lang.String REBOOT = "android.permission.REBOOT";
field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED";
+ field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS";
field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
@@ -197,7 +198,6 @@
public static final class R.attr {
ctor public R.attr();
- field public static final int abiOverride = 16844054; // 0x1010516
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -1364,6 +1364,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
+ field public static final int use32bitAbi = 16844054; // 0x1010516
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -23006,7 +23007,7 @@
method public void notifyRecordingStopped(android.net.Uri);
method public abstract void onConnect(android.net.Uri);
method public abstract void onDisconnect();
- method public abstract void onStartRecording();
+ method public abstract void onStartRecording(android.net.Uri);
method public abstract void onStopRecording();
}
@@ -23052,7 +23053,7 @@
ctor public TvRecordingClient(android.content.Context, java.lang.String, android.media.tv.TvRecordingClient.RecordingCallback, android.os.Handler);
method public void connect(java.lang.String, android.net.Uri);
method public void disconnect();
- method public void startRecording();
+ method public void startRecording(android.net.Uri);
method public void stopRecording();
}
@@ -40679,6 +40680,21 @@
method public static android.view.FocusFinder getInstance();
}
+ public final class FrameMetrics {
+ ctor public FrameMetrics(android.view.FrameMetrics);
+ method public long getMetric(int);
+ field public static final int ANIMATION_DURATION = 2; // 0x2
+ field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+ field public static final int DRAW_DURATION = 4; // 0x4
+ field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+ field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+ field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
+ field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
+ field public static final int SYNC_DURATION = 5; // 0x5
+ field public static final int TOTAL_DURATION = 8; // 0x8
+ field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+ }
+
public abstract class FrameStats {
ctor public FrameStats();
method public final long getEndTimeNano();
@@ -43182,6 +43198,7 @@
ctor public Window(android.content.Context);
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public void addFlags(int);
+ method public final void addFrameMetricsListener(android.view.Window.FrameMetricsListener, android.os.Handler);
method public void clearFlags(int);
method public abstract void closeAllPanels();
method public abstract void closePanel(int);
@@ -43233,6 +43250,7 @@
method public abstract boolean performContextMenuIdentifierAction(int, int);
method public abstract boolean performPanelIdentifierAction(int, int, int);
method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+ method public final void removeFrameMetricsListener(android.view.Window.FrameMetricsListener);
method public boolean requestFeature(int);
method public abstract void restoreHierarchyState(android.os.Bundle);
method public abstract android.os.Bundle saveHierarchyState();
@@ -43358,6 +43376,10 @@
method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
+ public static abstract interface Window.FrameMetricsListener {
+ method public abstract void onMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
+ }
+
public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
}
@@ -58034,8 +58056,11 @@
method public static boolean equals(java.lang.Object, java.lang.Object);
method public static int hash(java.lang.Object...);
method public static int hashCode(java.lang.Object);
+ method public static boolean isNull(java.lang.Object);
+ method public static boolean nonNull(java.lang.Object);
method public static T requireNonNull(T);
method public static T requireNonNull(T, java.lang.String);
+ method public static T requireNonNull(T, java.util.function.Supplier<java.lang.String>);
method public static java.lang.String toString(java.lang.Object);
method public static java.lang.String toString(java.lang.Object, java.lang.String);
}
@@ -59740,6 +59765,217 @@
}
+package java.util.function {
+
+ public abstract interface BiConsumer {
+ method public abstract void accept(T, U);
+ method public default java.util.function.BiConsumer<T, U> andThen(java.util.function.BiConsumer<? super T, ? super U>);
+ }
+
+ public abstract interface BiFunction {
+ method public default java.util.function.BiFunction<T, U, V> andThen(java.util.function.Function<? super R, ? extends V>);
+ method public abstract R apply(T, U);
+ }
+
+ public abstract interface BiPredicate {
+ method public default java.util.function.BiPredicate<T, U> and(java.util.function.BiPredicate<? super T, ? super U>);
+ method public default java.util.function.BiPredicate<T, U> negate();
+ method public default java.util.function.BiPredicate<T, U> or(java.util.function.BiPredicate<? super T, ? super U>);
+ method public abstract boolean test(T, U);
+ }
+
+ public abstract interface BinaryOperator implements java.util.function.BiFunction {
+ method public static java.util.function.BinaryOperator<T> maxBy(java.util.Comparator<? super T>);
+ method public static java.util.function.BinaryOperator<T> minBy(java.util.Comparator<? super T>);
+ }
+
+ public abstract interface BooleanSupplier {
+ method public abstract boolean getAsBoolean();
+ }
+
+ public abstract interface Consumer {
+ method public abstract void accept(T);
+ method public default java.util.function.Consumer<T> andThen(java.util.function.Consumer<? super T>);
+ }
+
+ public abstract interface DoubleBinaryOperator {
+ method public abstract double applyAsDouble(double, double);
+ }
+
+ public abstract interface DoubleConsumer {
+ method public abstract void accept(double);
+ method public default java.util.function.DoubleConsumer andThen(java.util.function.DoubleConsumer);
+ }
+
+ public abstract interface DoubleFunction {
+ method public abstract R apply(double);
+ }
+
+ public abstract interface DoublePredicate {
+ method public default java.util.function.DoublePredicate and(java.util.function.DoublePredicate);
+ method public default java.util.function.DoublePredicate negate();
+ method public default java.util.function.DoublePredicate or(java.util.function.DoublePredicate);
+ method public abstract boolean test(double);
+ }
+
+ public abstract interface DoubleSupplier {
+ method public abstract double getAsDouble();
+ }
+
+ public abstract interface DoubleToIntFunction {
+ method public abstract int applyAsInt(double);
+ }
+
+ public abstract interface DoubleToLongFunction {
+ method public abstract long applyAsLong(double);
+ }
+
+ public abstract interface DoubleUnaryOperator {
+ method public default java.util.function.DoubleUnaryOperator andThen(java.util.function.DoubleUnaryOperator);
+ method public abstract double applyAsDouble(double);
+ method public default java.util.function.DoubleUnaryOperator compose(java.util.function.DoubleUnaryOperator);
+ method public static java.util.function.DoubleUnaryOperator identity();
+ }
+
+ public abstract interface Function {
+ method public default java.util.function.Function<T, V> andThen(java.util.function.Function<? super R, ? extends V>);
+ method public abstract R apply(T);
+ method public default java.util.function.Function<V, R> compose(java.util.function.Function<? super V, ? extends T>);
+ method public static java.util.function.Function<T, T> identity();
+ }
+
+ public abstract interface IntBinaryOperator {
+ method public abstract int applyAsInt(int, int);
+ }
+
+ public abstract interface IntConsumer {
+ method public abstract void accept(int);
+ method public default java.util.function.IntConsumer andThen(java.util.function.IntConsumer);
+ }
+
+ public abstract interface IntFunction {
+ method public abstract R apply(int);
+ }
+
+ public abstract interface IntPredicate {
+ method public default java.util.function.IntPredicate and(java.util.function.IntPredicate);
+ method public default java.util.function.IntPredicate negate();
+ method public default java.util.function.IntPredicate or(java.util.function.IntPredicate);
+ method public abstract boolean test(int);
+ }
+
+ public abstract interface IntSupplier {
+ method public abstract int getAsInt();
+ }
+
+ public abstract interface IntToDoubleFunction {
+ method public abstract double applyAsDouble(int);
+ }
+
+ public abstract interface IntToLongFunction {
+ method public abstract long applyAsLong(int);
+ }
+
+ public abstract interface IntUnaryOperator {
+ method public default java.util.function.IntUnaryOperator andThen(java.util.function.IntUnaryOperator);
+ method public abstract int applyAsInt(int);
+ method public default java.util.function.IntUnaryOperator compose(java.util.function.IntUnaryOperator);
+ method public static java.util.function.IntUnaryOperator identity();
+ }
+
+ public abstract interface LongBinaryOperator {
+ method public abstract long applyAsLong(long, long);
+ }
+
+ public abstract interface LongConsumer {
+ method public abstract void accept(long);
+ method public default java.util.function.LongConsumer andThen(java.util.function.LongConsumer);
+ }
+
+ public abstract interface LongFunction {
+ method public abstract R apply(long);
+ }
+
+ public abstract interface LongPredicate {
+ method public default java.util.function.LongPredicate and(java.util.function.LongPredicate);
+ method public default java.util.function.LongPredicate negate();
+ method public default java.util.function.LongPredicate or(java.util.function.LongPredicate);
+ method public abstract boolean test(long);
+ }
+
+ public abstract interface LongSupplier {
+ method public abstract long getAsLong();
+ }
+
+ public abstract interface LongToDoubleFunction {
+ method public abstract double applyAsDouble(long);
+ }
+
+ public abstract interface LongToIntFunction {
+ method public abstract int applyAsInt(long);
+ }
+
+ public abstract interface LongUnaryOperator {
+ method public default java.util.function.LongUnaryOperator andThen(java.util.function.LongUnaryOperator);
+ method public abstract long applyAsLong(long);
+ method public default java.util.function.LongUnaryOperator compose(java.util.function.LongUnaryOperator);
+ method public static java.util.function.LongUnaryOperator identity();
+ }
+
+ public abstract interface ObjDoubleConsumer {
+ method public abstract void accept(T, double);
+ }
+
+ public abstract interface ObjIntConsumer {
+ method public abstract void accept(T, int);
+ }
+
+ public abstract interface ObjLongConsumer {
+ method public abstract void accept(T, long);
+ }
+
+ public abstract interface Predicate {
+ method public default java.util.function.Predicate<T> and(java.util.function.Predicate<? super T>);
+ method public static java.util.function.Predicate<T> isEqual(java.lang.Object);
+ method public default java.util.function.Predicate<T> negate();
+ method public default java.util.function.Predicate<T> or(java.util.function.Predicate<? super T>);
+ method public abstract boolean test(T);
+ }
+
+ public abstract interface Supplier {
+ method public abstract T get();
+ }
+
+ public abstract interface ToDoubleBiFunction {
+ method public abstract double applyAsDouble(T, U);
+ }
+
+ public abstract interface ToDoubleFunction {
+ method public abstract double applyAsDouble(T);
+ }
+
+ public abstract interface ToIntBiFunction {
+ method public abstract int applyAsInt(T, U);
+ }
+
+ public abstract interface ToIntFunction {
+ method public abstract int applyAsInt(T);
+ }
+
+ public abstract interface ToLongBiFunction {
+ method public abstract long applyAsLong(T, U);
+ }
+
+ public abstract interface ToLongFunction {
+ method public abstract long applyAsLong(T);
+ }
+
+ public abstract interface UnaryOperator implements java.util.function.Function {
+ method public static java.util.function.UnaryOperator<T> identity();
+ }
+
+}
+
package java.util.jar {
public class Attributes implements java.lang.Cloneable java.util.Map {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index acc68cf..6206323 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1126,14 +1126,19 @@
}
}
+ private byte[] argToBytes(String arg) {
+ if (arg.equals("!")) {
+ return null;
+ } else {
+ return HexDump.hexStringToByteArray(arg);
+ }
+ }
+
private void runUnlockUser() throws Exception {
int userId = Integer.parseInt(nextArgRequired());
- String tokenHex = nextArg();
- byte[] token = null;
- if (tokenHex != null) {
- token = HexDump.hexStringToByteArray(tokenHex);
- }
- boolean success = mAm.unlockUser(userId, token);
+ byte[] token = argToBytes(nextArgRequired());
+ byte[] secret = argToBytes(nextArgRequired());
+ boolean success = mAm.unlockUser(userId, token, secret);
if (success) {
System.out.println("Success: user unlocked");
} else {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index c96cca2..4bc6b97 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -624,8 +624,7 @@
gesture, 100);
try {
synchronized (mLock) {
- connection.sendMotionEvents(++mGestureStatusCallbackSequence,
- new ParceledListSlice<>(events));
+ mGestureStatusCallbackSequence++;
if (callback != null) {
if (mGestureStatusCallbackInfos == null) {
mGestureStatusCallbackInfos = new SparseArray<>();
@@ -634,6 +633,8 @@
callback, handler);
mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo);
}
+ connection.sendMotionEvents(mGestureStatusCallbackSequence,
+ new ParceledListSlice<>(events));
}
} catch (RemoteException re) {
throw new RuntimeException(re);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a3160f4..1954774 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2080,7 +2080,8 @@
data.enforceInterface(IActivityManager.descriptor);
int userId = data.readInt();
byte[] token = data.createByteArray();
- boolean result = unlockUser(userId, token);
+ byte[] secret = data.createByteArray();
+ boolean result = unlockUser(userId, token, secret);
reply.writeNoException();
reply.writeInt(result ? 1 : 0);
return true;
@@ -5571,12 +5572,13 @@
return result;
}
- public boolean unlockUser(int userId, byte[] token) throws RemoteException {
+ public boolean unlockUser(int userId, byte[] token, byte[] secret) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userId);
data.writeByteArray(token);
+ data.writeByteArray(secret);
mRemote.transact(IActivityManager.UNLOCK_USER_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6424520..04883a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5021,6 +5021,9 @@
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
+ // This cache location probably points at credential-encrypted
+ // storage which may not be accessible yet; assign it anyway instead
+ // of pointing at device-encrypted storage.
final File cacheDir = appContext.getCacheDir();
if (cacheDir != null) {
// Provide a usable directory for temporary files
@@ -5030,8 +5033,12 @@
+ "due to missing cache directory");
}
- // Use codeCacheDir to store generated/compiled graphics code and jit profiling data.
- final File codeCacheDir = appContext.getCodeCacheDir();
+ // Setup a location to store generated/compiled graphics code and
+ // JIT profiling data. Note that this data is stored in a
+ // device-encrypted storage area, so these caches must never contain
+ // user sensitive user data.
+ final Context deviceContext = appContext.createDeviceEncryptedStorageContext();
+ final File codeCacheDir = deviceContext.getCodeCacheDir();
if (codeCacheDir != null) {
setupGraphicsSupport(data.info, codeCacheDir);
setupJitProfileSupport(data.info, codeCacheDir);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 89d4931..eec503b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -58,6 +58,9 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.storage.IMountService;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
@@ -482,21 +485,20 @@
return f.delete();
}
- // Common-path handling of app data dir creation
+ /**
+ * Common-path handling of app data dir creation
+ */
private static File ensurePrivateDirExists(File file) {
if (!file.exists()) {
- if (!file.mkdirs()) {
- if (file.exists()) {
- // spurious failure; probably racing with another process for this app
- return file;
+ try {
+ Os.mkdir(file.getAbsolutePath(), 0771);
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EEXIST) {
+ // We must have raced with someone; that's okay
+ } else {
+ Log.w(TAG, "Failed to ensure " + file + ": " + e.getMessage());
}
- Log.w(TAG, "Failed to ensure directory " + file.getAbsolutePath());
- return null;
}
- FileUtils.setPermissions(
- file.getPath(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- -1, -1);
}
return file;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f5e7d78..b5ca6ee 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -426,7 +426,7 @@
// Multi-user APIs
public boolean switchUser(int userid) throws RemoteException;
public boolean startUserInBackground(int userid) throws RemoteException;
- public boolean unlockUser(int userid, byte[] token) throws RemoteException;
+ public boolean unlockUser(int userid, byte[] token, byte[] secret) throws RemoteException;
public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
public boolean isUserRunning(int userid, int flags) throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 9a88f2c..33fd1db 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1842,13 +1842,10 @@
* @see UiAutomation
*/
public UiAutomation getUiAutomation() {
- if (mUiAutomationConnection != null) {
- if (mUiAutomation == null) {
- return getUiAutomation(0);
- }
- return mUiAutomation;
+ if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) {
+ return getUiAutomation(0);
}
- return null;
+ return mUiAutomation;
}
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1fea665..c79dae5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -181,7 +181,7 @@
public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
/**
- * Value for {@link #flags}: this is set of the application has specified
+ * Value for {@link #flags}: this is set if the application has specified
* {@link android.R.styleable#AndroidManifestApplication_testOnly
* android:testOnly} to be true.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index aac0043..08deedb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4489,7 +4489,7 @@
PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
if ((flags & GET_SIGNATURES) != 0) {
- parser.collectCertificates(pkg, 0);
+ PackageParser.collectCertificates(pkg, 0);
}
PackageUserState state = new PackageUserState();
return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3802db8..5dddebd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -118,6 +118,8 @@
private static final boolean DEBUG_PARSER = false;
private static final boolean DEBUG_BACKUP = false;
+ private static final int MAX_PACKAGES_PER_APK = 5;
+
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
@@ -133,6 +135,50 @@
/** Path prefix for apps on expanded storage */
private static final String MNT_EXPAND = "/mnt/expand/";
+ private static final String TAG_MANIFEST = "manifest";
+ private static final String TAG_APPLICATION = "application";
+ private static final String TAG_OVERLAY = "overlay";
+ private static final String TAG_KEY_SETS = "key-sets";
+ private static final String TAG_PERMISSION_GROUP = "permission-group";
+ private static final String TAG_PERMISSION = "permission";
+ private static final String TAG_PERMISSION_TREE = "permission-tree";
+ private static final String TAG_USES_PERMISSION = "uses-permission";
+ private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
+ private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
+ private static final String TAG_USES_CONFIGURATION = "uses-configuration";
+ private static final String TAG_USES_FEATURE = "uses-feature";
+ private static final String TAG_FEATURE_GROUP = "feature-group";
+ private static final String TAG_USES_SDK = "uses-sdk";
+ private static final String TAG_SUPPORT_SCREENS = "supports-screens";
+ private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
+ private static final String TAG_INSTRUMENTATION = "instrumentation";
+ private static final String TAG_ORIGINAL_PACKAGE = "original-package";
+ private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
+ private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
+ private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
+ private static final String TAG_SUPPORTS_INPUT = "supports-input";
+ private static final String TAG_EAT_COMMENT = "eat-comment";
+ private static final String TAG_PACKAGE = "package";
+
+ // These are the tags supported by child packages
+ private static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
+ static {
+ CHILD_PACKAGE_TAGS.add(TAG_APPLICATION);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE);
+ CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_SDK);
+ CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS);
+ CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION);
+ CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE);
+ CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS);
+ CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT);
+ CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
+ }
+
/** @hide */
public static class NewPermissionInfo {
public final String name;
@@ -291,7 +337,7 @@
public final boolean coreApp;
public final boolean multiArch;
- public final String abiOverride;
+ public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
@@ -308,7 +354,7 @@
this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
- this.abiOverride = baseApk.abiOverride;
+ this.use32bitAbi = baseApk.use32bitAbi;
this.extractNativeLibs = baseApk.extractNativeLibs;
}
@@ -336,12 +382,12 @@
public final Signature[] signatures;
public final boolean coreApp;
public final boolean multiArch;
- public final String abiOverride;
+ public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- Signature[] signatures, boolean coreApp, boolean multiArch, String abiOverride,
+ Signature[] signatures, boolean coreApp, boolean multiArch, boolean use32bitAbi,
boolean extractNativeLibs) {
this.codePath = codePath;
this.packageName = packageName;
@@ -353,7 +399,7 @@
this.signatures = signatures;
this.coreApp = coreApp;
this.multiArch = multiArch;
- this.abiOverride = abiOverride;
+ this.use32bitAbi = use32bitAbi;
this.extractNativeLibs = extractNativeLibs;
}
}
@@ -796,8 +842,8 @@
}
}
- pkg.codePath = packageDir.getAbsolutePath();
- pkg.cpuAbiOverride = lite.abiOverride;
+ pkg.setCodePath(packageDir.getAbsolutePath());
+ pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
@@ -827,8 +873,8 @@
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
- pkg.codePath = apkFile.getAbsolutePath();
- pkg.cpuAbiOverride = lite.abiOverride;
+ pkg.setCodePath(apkFile.getAbsolutePath());
+ pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
@@ -885,10 +931,10 @@
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
- pkg.volumeUuid = volumeUuid;
- pkg.applicationInfo.volumeUuid = volumeUuid;
- pkg.baseCodePath = apkPath;
- pkg.mSignatures = null;
+ pkg.setVolumeUuid(volumeUuid);
+ pkg.setApplicationVolumeUuid(volumeUuid);
+ pkg.setBaseCodePath(apkPath);
+ pkg.setSignatures(null);
return pkg;
@@ -951,7 +997,7 @@
AttributeSet attrs = parser;
// We parsed manifest tag earlier; just skip past it
- parsePackageSplitNames(parser, attrs, flags);
+ parsePackageSplitNames(parser, attrs);
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
@@ -984,7 +1030,7 @@
}
foundApp = true;
- if (!parseSplitApplication(pkg, res, parser, attrs, flags, splitIndex, outError)) {
+ if (!parseSplitApplication(pkg, res, parser, flags, splitIndex, outError)) {
return null;
}
@@ -1016,7 +1062,18 @@
* populating {@link Package#mSignatures}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
- public void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
+ public static void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
+ collectCertificatesInternal(pkg, parseFlags);
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ Package childPkg = pkg.childPackages.get(i);
+ childPkg.mCertificates = pkg.mCertificates;
+ childPkg.mSignatures = pkg.mSignatures;
+ childPkg.mSigningKeys = pkg.mSigningKeys;
+ }
+ }
+
+ private static void collectCertificatesInternal(Package pkg, int parseFlags) throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
@@ -1269,7 +1326,7 @@
}
private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
- AttributeSet attrs, int flags) throws IOException, XmlPullParserException,
+ AttributeSet attrs) throws IOException, XmlPullParserException,
PackageParserException {
int type;
@@ -1281,7 +1338,7 @@
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No start tag found");
}
- if (!parser.getName().equals("manifest")) {
+ if (!parser.getName().equals(TAG_MANIFEST)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No <manifest> tag");
}
@@ -1315,14 +1372,14 @@
private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
XmlPullParserException, PackageParserException {
- final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
+ final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
- String abiOverride = null;
+ boolean use32bitAbi = false;
boolean extractNativeLibs = true;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
@@ -1363,8 +1420,8 @@
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
}
- if ("abiOverride".equals(attr)) {
- abiOverride = attrs.getAttributeValue(i);
+ if ("use32bitAbi".equals(attr)) {
+ use32bitAbi = attrs.getAttributeBooleanValue(i, false);
}
if ("extractNativeLibs".equals(attr)) {
extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
@@ -1375,7 +1432,7 @@
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
- abiOverride, extractNativeLibs);
+ use32bitAbi, extractNativeLibs);
}
/**
@@ -1391,46 +1448,120 @@
}
/**
- * Parse the manifest of a <em>base APK</em>.
- * <p>
- * When adding new features, carefully consider if they should also be
- * supported by split APKs.
+ * Parses a child package and adds it to the parent if successful. If you add
+ * new tags that need to be supported by child packages make sure to add them
+ * to {@link #CHILD_PACKAGE_TAGS}.
+ *
+ * @param parentPkg The parent that contains the child
+ * @param res Resources against which to resolve values
+ * @param parser Parser of the manifest
+ * @param flags Flags about how to parse
+ * @param outError Human readable error if parsing fails
+ * @return True of parsing succeeded.
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private boolean parseBaseApkChild(Package parentPkg, Resources res, XmlResourceParser parser,
+ int flags, String[] outError) throws XmlPullParserException, IOException {
+ // Let ppl not abuse this mechanism by limiting the packages per APK
+ if (parentPkg.childPackages != null && parentPkg.childPackages.size() + 2
+ > MAX_PACKAGES_PER_APK) {
+ outError[0] = "Maximum number of packages per APK is: " + MAX_PACKAGES_PER_APK;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return false;
+ }
+
+ // Make sure we have a valid child package name
+ String childPackageName = parser.getAttributeValue(null, "package");
+ if (validateName(childPackageName, true, false) != null) {
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+ return false;
+ }
+
+ // Child packages must be unique
+ if (childPackageName.equals(parentPkg.packageName)) {
+ String message = "Child package name cannot be equal to parent package name: "
+ + parentPkg.packageName;
+ Slog.w(TAG, message);
+ outError[0] = message;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return false;
+ }
+
+ // Child packages must be unique
+ if (parentPkg.hasChildPackage(childPackageName)) {
+ String message = "Duplicate child package:" + childPackageName;
+ Slog.w(TAG, message);
+ outError[0] = message;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return false;
+ }
+
+ // Go ahead and parse the child
+ Package childPkg = new Package(childPackageName);
+
+ // Child package inherits parent version code/name/target SDK
+ childPkg.mVersionCode = parentPkg.mVersionCode;
+ childPkg.baseRevisionCode = parentPkg.baseRevisionCode;
+ childPkg.mVersionName = parentPkg.mVersionName;
+ childPkg.applicationInfo.targetSdkVersion = parentPkg.applicationInfo.targetSdkVersion;
+
+ childPkg = parseBaseApkCommon(childPkg, CHILD_PACKAGE_TAGS, res, parser, flags, outError);
+ if (childPkg == null) {
+ // If we got null then error was set during child parsing
+ return false;
+ }
+
+ // Set the parent-child relation
+ if (parentPkg.childPackages == null) {
+ parentPkg.childPackages = new ArrayList<>();
+ }
+ parentPkg.childPackages.add(childPkg);
+ childPkg.parentPackage = parentPkg;
+
+ return true;
+ }
+
+ /**
+ * Parse the manifest of a <em>base APK</em>. When adding new features you
+ * need to consider whether they should be supported by split APKs and child
+ * packages.
+ *
+ * @param res The resources from which to resolve values
+ * @param parser The manifest parser
+ * @param flags Flags how to parse
+ * @param outError Human readable error message
+ * @return Parsed package or null on error.
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
*/
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
- final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
-
- AttributeSet attrs = parser;
-
- mParseInstrumentationArgs = null;
- mParseActivityArgs = null;
- mParseServiceArgs = null;
- mParseProviderArgs = null;
-
- final String pkgName;
final String splitName;
+ final String pkgName;
+
try {
- Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
+ Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
pkgName = packageSplit.first;
splitName = packageSplit.second;
+
+ if (!TextUtils.isEmpty(splitName)) {
+ outError[0] = "Expected base APK, but found split " + splitName;
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+ return null;
+ }
} catch (PackageParserException e) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
- int type;
-
- if (!TextUtils.isEmpty(splitName)) {
- outError[0] = "Expected base APK, but found split " + splitName;
- mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
- return null;
- }
-
final Package pkg = new Package(pkgName);
- boolean foundApp = false;
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
+
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
@@ -1440,11 +1571,53 @@
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
+
+ pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+
+ sa.recycle();
+
+ return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
+ }
+
+ /**
+ * This is the common parsing routing for handling parent and child
+ * packages in a base APK. The difference between parent and child
+ * parsing is that some targs are not supported by child packages as
+ * well as some manifest attributes are ignored. The implementation
+ * assumes the calling code already handled the manifest tag if needed
+ * (this applies to the parent only).
+ *
+ * @param pkg The package which to populate
+ * @param acceptedTags Which tags to handle, null to handle all
+ * @param res Resources against which to resolve values
+ * @param parser Parser of the manifest
+ * @param flags Flags about how to parse
+ * @param outError Human readable error if parsing fails
+ * @return The package if parsing succeeded or null.
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
+ XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
+ IOException {
+ final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
+ mParseInstrumentationArgs = null;
+ mParseActivityArgs = null;
+ mParseServiceArgs = null;
+ mParseProviderArgs = null;
+
+ int type;
+ boolean foundApp = false;
+
+ TypedArray sa = res.obtainAttributes(parser,
+ com.android.internal.R.styleable.AndroidManifest);
+
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
String nameError = validateName(str, true, false);
- if (nameError != null && !"android".equals(pkgName)) {
+ if (nameError != null && !"android".equals(pkg.packageName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
+ str + "\": " + nameError;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
@@ -1460,9 +1633,6 @@
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation = pkg.installLocation;
- pkg.coreApp = attrs.getAttributeBooleanValue(null, "coreApp", false);
-
- sa.recycle();
/* Set the global "forward lock" flag */
if ((flags & PARSE_FORWARD_LOCK) != 0) {
@@ -1494,7 +1664,16 @@
}
String tagName = parser.getName();
- if (tagName.equals("application")) {
+
+ if (acceptedTags != null && !acceptedTags.contains(tagName)) {
+ Slog.w(TAG, "Skipping unsupported element under <manifest>: "
+ + tagName + " at " + mArchiveSourcePath + " "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+
+ if (tagName.equals(TAG_APPLICATION)) {
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = "<manifest> has more than one <application>";
@@ -1508,13 +1687,13 @@
}
foundApp = true;
- if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
+ if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
- } else if (tagName.equals("overlay")) {
+ } else if (tagName.equals(TAG_OVERLAY)) {
pkg.mTrustedOverlay = trustedOverlay;
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
@@ -1536,34 +1715,34 @@
}
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("key-sets")) {
- if (!parseKeySets(pkg, res, parser, attrs, outError)) {
+ } else if (tagName.equals(TAG_KEY_SETS)) {
+ if (!parseKeySets(pkg, res, parser, outError)) {
return null;
}
- } else if (tagName.equals("permission-group")) {
- if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
+ } else if (tagName.equals(TAG_PERMISSION_GROUP)) {
+ if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
return null;
}
- } else if (tagName.equals("permission")) {
- if (parsePermission(pkg, res, parser, attrs, outError) == null) {
+ } else if (tagName.equals(TAG_PERMISSION)) {
+ if (parsePermission(pkg, res, parser, outError) == null) {
return null;
}
- } else if (tagName.equals("permission-tree")) {
- if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
+ } else if (tagName.equals(TAG_PERMISSION_TREE)) {
+ if (parsePermissionTree(pkg, res, parser, outError) == null) {
return null;
}
- } else if (tagName.equals("uses-permission")) {
- if (!parseUsesPermission(pkg, res, parser, attrs)) {
+ } else if (tagName.equals(TAG_USES_PERMISSION)) {
+ if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
- } else if (tagName.equals("uses-permission-sdk-m")
- || tagName.equals("uses-permission-sdk-23")) {
- if (!parseUsesPermission(pkg, res, parser, attrs)) {
+ } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
+ || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
+ if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
- } else if (tagName.equals("uses-configuration")) {
+ } else if (tagName.equals(TAG_USES_CONFIGURATION)) {
ConfigurationInfo cPref = new ConfigurationInfo();
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
cPref.reqTouchScreen = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
@@ -1589,8 +1768,8 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("uses-feature")) {
- FeatureInfo fi = parseUsesFeature(res, attrs);
+ } else if (tagName.equals(TAG_USES_FEATURE)) {
+ FeatureInfo fi = parseUsesFeature(res, parser);
pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
if (fi.name == null) {
@@ -1601,7 +1780,7 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("feature-group")) {
+ } else if (tagName.equals(TAG_FEATURE_GROUP)) {
FeatureGroupInfo group = new FeatureGroupInfo();
ArrayList<FeatureInfo> features = null;
final int innerDepth = parser.getDepth();
@@ -1613,7 +1792,7 @@
final String innerTagName = parser.getName();
if (innerTagName.equals("uses-feature")) {
- FeatureInfo featureInfo = parseUsesFeature(res, attrs);
+ FeatureInfo featureInfo = parseUsesFeature(res, parser);
// FeatureGroups are stricter and mandate that
// any <uses-feature> declared are mandatory.
featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
@@ -1632,9 +1811,9 @@
}
pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
- } else if (tagName.equals("uses-sdk")) {
+ } else if (tagName.equals(TAG_USES_SDK)) {
if (SDK_VERSION > 0) {
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesSdk);
int minVers = 0;
@@ -1723,8 +1902,8 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("supports-screens")) {
- sa = res.obtainAttributes(attrs,
+ } else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestSupportsScreens);
pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
@@ -1762,8 +1941,8 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("protected-broadcast")) {
- sa = res.obtainAttributes(attrs,
+ } else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
// Note: don't allow this value to be a reference to a resource
@@ -1784,13 +1963,12 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("instrumentation")) {
- if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
+ } else if (tagName.equals(TAG_INSTRUMENTATION)) {
+ if (parseInstrumentation(pkg, res, parser, outError) == null) {
return null;
}
-
- } else if (tagName.equals("original-package")) {
- sa = res.obtainAttributes(attrs,
+ } else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String orig =sa.getNonConfigurationString(
@@ -1807,8 +1985,8 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("adopt-permissions")) {
- sa = res.obtainAttributes(attrs,
+ } else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) {
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String name = sa.getNonConfigurationString(
@@ -1825,24 +2003,29 @@
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("uses-gl-texture")) {
+ } else if (tagName.equals(TAG_USES_GL_TEXTURE)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
- } else if (tagName.equals("compatible-screens")) {
+ } else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
- } else if (tagName.equals("supports-input")) {
+ } else if (tagName.equals(TAG_SUPPORTS_INPUT)) {//
XmlUtils.skipCurrentTag(parser);
continue;
- } else if (tagName.equals("eat-comment")) {
+ } else if (tagName.equals(TAG_EAT_COMMENT)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
+ } else if (tagName.equals(TAG_PACKAGE)) {
+ if (!parseBaseApkChild(pkg, res, parser, flags, outError)) {
+ // If parsing a child failed the error is already set
+ return null;
+ }
} else if (RIGID_PARSER) {
outError[0] = "Bad element under <manifest>: "
+ parser.getName();
@@ -1956,9 +2139,9 @@
return fi;
}
- private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
- AttributeSet attrs) throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
// Note: don't allow this value to be a reference to a resource
@@ -2078,7 +2261,7 @@
}
private boolean parseKeySets(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, String[] outError)
+ XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
// we've encountered the 'key-sets' tag
// all the keys and keysets that we want must be defined here
@@ -2108,7 +2291,7 @@
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
- final TypedArray sa = res.obtainAttributes(attrs,
+ final TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestKeySet);
final String keysetName = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestKeySet_name);
@@ -2123,7 +2306,7 @@
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
- final TypedArray sa = res.obtainAttributes(attrs,
+ final TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPublicKey);
final String publicKeyName = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestPublicKey_name);
@@ -2164,7 +2347,7 @@
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("upgrade-key-set")) {
- final TypedArray sa = res.obtainAttributes(attrs,
+ final TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUpgradeKeySet);
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUpgradeKeySet_name);
@@ -2223,11 +2406,11 @@
}
private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res,
- XmlPullParser parser, AttributeSet attrs, String[] outError)
- throws XmlPullParserException, IOException {
+ XmlResourceParser parser, String[] outError)
+ throws XmlPullParserException, IOException {
PermissionGroup perm = new PermissionGroup(owner);
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermissionGroup);
if (!parsePackageItemInfo(owner, perm.info, outError,
@@ -2255,7 +2438,7 @@
sa.recycle();
- if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm,
+ if (!parseAllMetaData(res, parser, "<permission-group>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
@@ -2267,11 +2450,11 @@
}
private Permission parsePermission(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, String[] outError)
+ XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermission);
if (!parsePackageItemInfo(owner, perm.info, outError,
@@ -2325,8 +2508,7 @@
}
}
- if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
- outError)) {
+ if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
@@ -2337,11 +2519,11 @@
}
private Permission parsePermissionTree(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, String[] outError)
+ XmlResourceParser parser, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPermissionTree);
if (!parsePackageItemInfo(owner, perm.info, outError,
@@ -2373,7 +2555,7 @@
perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
perm.tree = true;
- if (!parseAllMetaData(res, parser, attrs, "<permission-tree>", perm,
+ if (!parseAllMetaData(res, parser, "<permission-tree>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
@@ -2385,9 +2567,9 @@
}
private Instrumentation parseInstrumentation(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, String[] outError)
- throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ XmlResourceParser parser, String[] outError)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestInstrumentation);
if (mParseInstrumentationArgs == null) {
@@ -2433,7 +2615,7 @@
return null;
}
- if (!parseAllMetaData(res, parser, attrs, "<instrumentation>", a,
+ if (!parseAllMetaData(res, parser, "<instrumentation>", a,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
@@ -2452,12 +2634,12 @@
* supported by split APKs.
*/
private boolean parseBaseApplication(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+ XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
String name = sa.getNonConfigurationString(
@@ -2608,10 +2790,13 @@
ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
}
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
- false)) {
- ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
+ // The parent package controls installation, hence specify test only installs.
+ if (owner.parentPackage == null) {
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
+ false)) {
+ ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
+ }
}
if (sa.getBoolean(
@@ -2736,7 +2921,7 @@
String tagName = parser.getName();
if (tagName.equals("activity")) {
- Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
+ Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
@@ -2746,7 +2931,7 @@
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
- Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
+ Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2755,7 +2940,7 @@
owner.receivers.add(a);
} else if (tagName.equals("service")) {
- Service s = parseService(owner, res, parser, attrs, flags, outError);
+ Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2764,7 +2949,7 @@
owner.services.add(s);
} else if (tagName.equals("provider")) {
- Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
+ Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2773,7 +2958,7 @@
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
- Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
+ Activity a = parseActivityAlias(owner, res, parser, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2785,14 +2970,14 @@
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
- if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
+ if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("library")) {
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestLibrary);
// Note: don't allow this value to be a reference to a resource
@@ -2812,7 +2997,7 @@
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-library")) {
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
@@ -2913,10 +3098,10 @@
* of doing, so many valid features of a base APK have been carefully
* omitted here.
*/
- private boolean parseSplitApplication(Package owner, Resources res, XmlPullParser parser,
- AttributeSet attrs, int flags, int splitIndex, String[] outError)
+ private boolean parseSplitApplication(Package owner, Resources res, XmlResourceParser parser,
+ int flags, int splitIndex, String[] outError)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
if (sa.getBoolean(
@@ -2934,7 +3119,7 @@
String tagName = parser.getName();
if (tagName.equals("activity")) {
- Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
+ Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
@@ -2944,7 +3129,7 @@
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
- Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
+ Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2953,7 +3138,7 @@
owner.receivers.add(a);
} else if (tagName.equals("service")) {
- Service s = parseService(owner, res, parser, attrs, flags, outError);
+ Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2962,7 +3147,7 @@
owner.services.add(s);
} else if (tagName.equals("provider")) {
- Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
+ Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2971,7 +3156,7 @@
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
- Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
+ Activity a = parseActivityAlias(owner, res, parser, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -2983,14 +3168,14 @@
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
- if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
+ if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("uses-library")) {
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
@@ -3086,10 +3271,10 @@
}
private Activity parseActivity(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
+ XmlResourceParser parser, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity);
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
if (mParseActivityArgs == null) {
mParseActivityArgs = new ParseComponentArgs(owner, outError,
@@ -3327,7 +3512,7 @@
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
+ if (!parseIntent(res, parser, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -3339,7 +3524,7 @@
}
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, attrs, false, false, intent, outError)) {
+ if (!parseIntent(res, parser, false, false, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -3353,12 +3538,12 @@
owner.preferredActivityFilters.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
- if ((a.metaData = parseMetaData(res, parser, attrs, a.metaData,
+ if ((a.metaData = parseMetaData(res, parser, a.metaData,
outError)) == null) {
return null;
}
} else if (!receiver && parser.getName().equals("layout")) {
- parseLayout(res, attrs, a);
+ parseLayout(res, parser, a);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
@@ -3432,9 +3617,9 @@
}
private Activity parseActivityAlias(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+ XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestActivityAlias);
String targetActivity = sa.getNonConfigurationString(
@@ -3565,7 +3750,7 @@
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
- if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
+ if (!parseIntent(res, parser, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
@@ -3576,7 +3761,7 @@
a.intents.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
- if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
+ if ((a.metaData=parseMetaData(res, parser, a.metaData,
outError)) == null) {
return null;
}
@@ -3602,9 +3787,9 @@
}
private Provider parseProvider(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+ XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestProvider);
if (mParseProviderArgs == null) {
@@ -3731,7 +3916,7 @@
}
p.info.authority = cpname.intern();
- if (!parseProviderTags(res, parser, attrs, p, outError)) {
+ if (!parseProviderTags(res, parser, p, outError)) {
return null;
}
@@ -3739,8 +3924,7 @@
}
private boolean parseProviderTags(Resources res,
- XmlPullParser parser, AttributeSet attrs,
- Provider outInfo, String[] outError)
+ XmlResourceParser parser, Provider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -3753,19 +3937,19 @@
if (parser.getName().equals("intent-filter")) {
ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
- if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
+ if (!parseIntent(res, parser, true, false, intent, outError)) {
return false;
}
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
- if ((outInfo.metaData=parseMetaData(res, parser, attrs,
+ if ((outInfo.metaData=parseMetaData(res, parser,
outInfo.metaData, outError)) == null) {
return false;
}
} else if (parser.getName().equals("grant-uri-permission")) {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
PatternMatcher pa = null;
@@ -3817,7 +4001,7 @@
XmlUtils.skipCurrentTag(parser);
} else if (parser.getName().equals("path-permission")) {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestPathPermission);
PathPermission pa = null;
@@ -3922,9 +4106,9 @@
}
private Service parseService(Package owner, Resources res,
- XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
+ XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestService);
if (mParseServiceArgs == null) {
@@ -4025,13 +4209,13 @@
if (parser.getName().equals("intent-filter")) {
ServiceIntentInfo intent = new ServiceIntentInfo(s);
- if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
+ if (!parseIntent(res, parser, true, false, intent, outError)) {
return null;
}
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
- if ((s.metaData=parseMetaData(res, parser, attrs, s.metaData,
+ if ((s.metaData=parseMetaData(res, parser, s.metaData,
outError)) == null) {
return null;
}
@@ -4056,10 +4240,8 @@
return s;
}
- private boolean parseAllMetaData(Resources res,
- XmlPullParser parser, AttributeSet attrs, String tag,
- Component<?> outInfo, String[] outError)
- throws XmlPullParserException, IOException {
+ private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
+ Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4070,7 +4252,7 @@
}
if (parser.getName().equals("meta-data")) {
- if ((outInfo.metaData=parseMetaData(res, parser, attrs,
+ if ((outInfo.metaData=parseMetaData(res, parser,
outInfo.metaData, outError)) == null) {
return false;
}
@@ -4091,11 +4273,10 @@
}
private Bundle parseMetaData(Resources res,
- XmlPullParser parser, AttributeSet attrs,
- Bundle data, String[] outError)
+ XmlResourceParser parser, Bundle data, String[] outError)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestMetaData);
if (data == null) {
@@ -4234,11 +4415,11 @@
private static final String ANDROID_RESOURCES
= "http://schemas.android.com/apk/res/android";
- private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs,
+ private boolean parseIntent(Resources res, XmlResourceParser parser,
boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(attrs,
+ TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestIntentFilter);
int priority = sa.getInt(
@@ -4278,7 +4459,7 @@
String nodeName = parser.getName();
if (nodeName.equals("action")) {
- String value = attrs.getAttributeValue(
+ String value = parser.getAttributeValue(
ANDROID_RESOURCES, "name");
if (value == null || value == "") {
outError[0] = "No value supplied for <android:name>";
@@ -4288,7 +4469,7 @@
outInfo.addAction(value);
} else if (nodeName.equals("category")) {
- String value = attrs.getAttributeValue(
+ String value = parser.getAttributeValue(
ANDROID_RESOURCES, "name");
if (value == null || value == "") {
outError[0] = "No value supplied for <android:name>";
@@ -4299,7 +4480,7 @@
outInfo.addCategory(value);
} else if (nodeName.equals("data")) {
- sa = res.obtainAttributes(attrs,
+ sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestData);
String str = sa.getNonConfigurationString(
@@ -4464,6 +4645,9 @@
public ArrayList<String> protectedBroadcasts;
+ public Package parentPackage;
+ public ArrayList<Package> childPackages;
+
public ArrayList<String> libraryNames = null;
public ArrayList<String> usesLibraries = null;
public ArrayList<String> usesOptionalLibraries = null;
@@ -4555,6 +4739,12 @@
* and prods fields out of {@code this.applicationInfo}.
*/
public String cpuAbiOverride;
+ /**
+ * The install time abi override to choose 32bit abi's when multiple abi's
+ * are present. This is only meaningfull for multiarch applications.
+ * The use32bitAbi attribute is ignored if cpuAbiOverride is also set.
+ */
+ public boolean use32bitAbi;
public Package(String packageName) {
this.packageName = packageName;
@@ -4562,6 +4752,141 @@
applicationInfo.uid = -1;
}
+ public void setApplicationVolumeUuid(String volumeUuid) {
+ this.applicationInfo.volumeUuid = volumeUuid;
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).applicationInfo.volumeUuid = volumeUuid;
+ }
+ }
+ }
+
+ public void setApplicationInfoCodePath(String codePath) {
+ this.applicationInfo.setCodePath(codePath);
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).applicationInfo.setCodePath(codePath);
+ }
+ }
+ }
+
+ public void setApplicationInfoResourcePath(String resourcePath) {
+ this.applicationInfo.setResourcePath(resourcePath);
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).applicationInfo.setResourcePath(resourcePath);
+ }
+ }
+ }
+
+ public void setApplicationInfoBaseResourcePath(String resourcePath) {
+ this.applicationInfo.setBaseResourcePath(resourcePath);
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).applicationInfo.setBaseResourcePath(resourcePath);
+ }
+ }
+ }
+
+ public void setApplicationInfoBaseCodePath(String baseCodePath) {
+ this.applicationInfo.setBaseCodePath(baseCodePath);
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).applicationInfo.setBaseCodePath(baseCodePath);
+ }
+ }
+ }
+
+ public boolean hasChildPackage(String packageName) {
+ final int childCount = (childPackages != null) ? childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ if (childPackages.get(i).packageName.equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setApplicationInfoSplitCodePaths(String[] splitCodePaths) {
+ this.applicationInfo.setSplitCodePaths(splitCodePaths);
+ // Children have no splits
+ }
+
+ public void setApplicationInfoSplitResourcePaths(String[] resroucePaths) {
+ this.applicationInfo.setSplitResourcePaths(resroucePaths);
+ // Children have no splits
+ }
+
+ public void setSplitCodePaths(String[] codePaths) {
+ this.splitCodePaths = codePaths;
+ }
+
+ public void setCodePath(String codePath) {
+ this.codePath = codePath;
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).codePath = codePath;
+ }
+ }
+ }
+
+ public void setBaseCodePath(String baseCodePath) {
+ this.baseCodePath = baseCodePath;
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).baseCodePath = baseCodePath;
+ }
+ }
+ }
+
+ public void setSignatures(Signature[] signatures) {
+ this.mSignatures = signatures;
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).mSignatures = signatures;
+ }
+ }
+ }
+
+ public void setVolumeUuid(String volumeUuid) {
+ this.volumeUuid = volumeUuid;
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).volumeUuid = volumeUuid;
+ }
+ }
+ }
+
+ public void setApplicationInfoFlags(int mask, int flags) {
+ applicationInfo.flags = (applicationInfo.flags & ~mask) | (mask & flags);
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).applicationInfo.flags =
+ (applicationInfo.flags & ~mask) | (mask & flags);
+ }
+ }
+ }
+
+ public void setUse32bitAbi(boolean use32bitAbi) {
+ this.use32bitAbi = use32bitAbi;
+ if (childPackages != null) {
+ final int packageCount = childPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ childPackages.get(i).use32bitAbi = use32bitAbi;
+ }
+ }
+ }
+
public List<String> getAllCodePaths() {
ArrayList<String> paths = new ArrayList<>();
paths.add(baseCodePath);
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
new file mode 100644
index 0000000..80e6146
--- /dev/null
+++ b/core/java/android/os/UpdateEngine.java
@@ -0,0 +1,148 @@
+/*
+ * 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 android.os;
+
+import android.annotation.SystemApi;
+import android.os.IUpdateEngine;
+import android.os.IUpdateEngineCallback;
+import android.os.RemoteException;
+
+import android.util.Log;
+
+/**
+ * UpdateEngine handles calls to the update engine which takes care of A/B OTA
+ * updates. It wraps up the update engine Binder APIs and exposes them as
+ * SystemApis, which will be called by system apps like GmsCore.
+ *
+ * The APIs defined in this class and UpdateEngineCallback class must be in
+ * sync with the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public class UpdateEngine {
+ private static final String TAG = "UpdateEngine";
+
+ private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService";
+
+ /**
+ * Error code from the update engine. Values must agree with the ones in
+ * system/update_engine/common/error_code.h.
+ */
+ @SystemApi
+ public static final class ErrorCodeConstants {
+ public static final int SUCCESS = 0;
+ public static final int ERROR = 1;
+ public static final int FILESYSTEM_COPIER_ERROR = 4;
+ public static final int POST_INSTALL_RUNNER_ERROR = 5;
+ public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6;
+ public static final int INSTALL_DEVICE_OPEN_ERROR = 7;
+ public static final int KERNEL_DEVICE_OPEN_ERROR = 8;
+ public static final int DOWNLOAD_TRANSFER_ERROR = 9;
+ public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
+ public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
+ public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+ }
+
+ /**
+ * Update status code from the update engine. Values must agree with the
+ * ones in system/update_engine/client_library/include/update_engine/update_status.h.
+ */
+ @SystemApi
+ public static final class UpdateStatusConstants {
+ public static final int IDLE = 0;
+ public static final int CHECKING_FOR_UPDATE = 1;
+ public static final int UPDATE_AVAILABLE = 2;
+ public static final int DOWNLOADING = 3;
+ public static final int VERIFYING = 4;
+ public static final int FINALIZING = 5;
+ public static final int UPDATED_NEED_REBOOT = 6;
+ public static final int REPORTING_ERROR_EVENT = 7;
+ public static final int ATTEMPTING_ROLLBACK = 8;
+ public static final int DISABLED = 9;
+ }
+
+ private IUpdateEngine mUpdateEngine;
+
+ @SystemApi
+ public UpdateEngine() {
+ mUpdateEngine = IUpdateEngine.Stub.asInterface(
+ ServiceManager.getService(UPDATE_ENGINE_SERVICE));
+ }
+
+ @SystemApi
+ public boolean bind(final UpdateEngineCallback callback, final Handler handler) throws RemoteException {
+ IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() {
+ @Override
+ public void onStatusUpdate(final int status, final float percent) {
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onStatusUpdate(status, percent);
+ }
+ });
+ } else {
+ callback.onStatusUpdate(status, percent);
+ }
+ }
+
+ @Override
+ public void onPayloadApplicationComplete(final int errorCode) {
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPayloadApplicationComplete(errorCode);
+ }
+ });
+ } else {
+ callback.onPayloadApplicationComplete(errorCode);
+ }
+ }
+ };
+
+ return mUpdateEngine.bind(updateEngineCallback);
+ }
+
+ @SystemApi
+ public boolean bind(final UpdateEngineCallback callback) throws RemoteException {
+ return bind(callback, null);
+ }
+
+ @SystemApi
+ public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) throws RemoteException {
+ mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs);
+ }
+
+ @SystemApi
+ public void cancel() throws RemoteException {
+ mUpdateEngine.cancel();
+ }
+
+ @SystemApi
+ public void suspend() throws RemoteException {
+ mUpdateEngine.suspend();
+ }
+
+ @SystemApi
+ public void resume() throws RemoteException {
+ mUpdateEngine.resume();
+ }
+}
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
new file mode 100644
index 0000000..b3b856f
--- /dev/null
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.os;
+
+import android.annotation.SystemApi;
+
+/**
+ * Callback function for UpdateEngine.
+ *
+ * The APIs defined in this class and UpdateEngine class must be in sync with
+ * the ones in
+ * system/update_engine/binder_bindings/android/os/IUpdateEngine.aidl and
+ * system/update_engine/binder_bindings/android/os/IUpdateEngineCallback.aidl.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class UpdateEngineCallback {
+
+ @SystemApi
+ public abstract void onStatusUpdate(int status, float percent);
+
+ @SystemApi
+ public abstract void onPayloadApplicationComplete(int errorCode);
+}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index dd8eb5f..fc440d2 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1233,7 +1233,8 @@
}
@Override
- public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException {
+ public void changeUserKey(int userId, int serialNumber,
+ byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
@@ -1241,6 +1242,27 @@
_data.writeInt(userId);
_data.writeInt(serialNumber);
_data.writeByteArray(token);
+ _data.writeByteArray(oldSecret);
+ _data.writeByteArray(newSecret);
+ mRemote.transact(Stub.TRANSACTION_changeUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber,
+ byte[] token, byte[] secret) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userId);
+ _data.writeInt(serialNumber);
+ _data.writeByteArray(token);
+ _data.writeByteArray(secret);
mRemote.transact(Stub.TRANSACTION_unlockUserKey, _data, _reply, 0);
_reply.readException();
} finally {
@@ -1448,6 +1470,8 @@
static final int TRANSACTION_mountAppFuse = IBinder.FIRST_CALL_TRANSACTION + 69;
+ static final int TRANSACTION_changeUserKey = IBinder.FIRST_CALL_TRANSACTION + 70;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -2026,12 +2050,24 @@
reply.writeNoException();
return true;
}
+ case TRANSACTION_changeUserKey: {
+ data.enforceInterface(DESCRIPTOR);
+ int userId = data.readInt();
+ int serialNumber = data.readInt();
+ byte[] token = data.createByteArray();
+ byte[] oldSecret = data.createByteArray();
+ byte[] newSecret = data.createByteArray();
+ changeUserKey(userId, serialNumber, token, oldSecret, newSecret);
+ reply.writeNoException();
+ return true;
+ }
case TRANSACTION_unlockUserKey: {
data.enforceInterface(DESCRIPTOR);
int userId = data.readInt();
int serialNumber = data.readInt();
byte[] token = data.createByteArray();
- unlockUserKey(userId, serialNumber, token);
+ byte[] secret = data.createByteArray();
+ unlockUserKey(userId, serialNumber, token, secret);
reply.writeNoException();
return true;
}
@@ -2383,8 +2419,11 @@
public void createUserKey(int userId, int serialNumber, boolean ephemeral)
throws RemoteException;
public void destroyUserKey(int userId) throws RemoteException;
+ public void changeUserKey(int userId, int serialNumber,
+ byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException;
- public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
+ public void unlockUserKey(int userId, int serialNumber,
+ byte[] token, byte[] secret) throws RemoteException;
public void lockUserKey(int userId) throws RemoteException;
public boolean isUserKeyUnlocked(int userId) throws RemoteException;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b82638a..e7dfbd7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -991,9 +991,9 @@
}
/** {@hide} */
- public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
try {
- mMountService.unlockUserKey(userId, serialNumber, token);
+ mMountService.unlockUserKey(userId, serialNumber, token, secret);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 287c696..b2e8d33 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -57,9 +57,9 @@
* tag <code>fade</code>, along with the standard
* attributes of {@link android.R.styleable#Fade} and
* {@link android.R.styleable#Transition}.</p>
-
*/
public class Fade extends Visibility {
+ static final String PROPNAME_TRANSITION_ALPHA = "android:fade:transitionAlpha";
private static boolean DBG = Transition.DBG && false;
@@ -105,6 +105,13 @@
setMode(fadingMode);
}
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ transitionValues.values.put(PROPNAME_TRANSITION_ALPHA,
+ transitionValues.view.getTransitionAlpha());
+ }
+
/**
* Utility method to handle creating and running the Animator.
*/
@@ -119,7 +126,6 @@
}
final FadeAnimatorListener listener = new FadeAnimatorListener(view);
anim.addListener(listener);
- anim.addPauseListener(listener);
addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
@@ -138,18 +144,28 @@
Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
startView + ", " + view);
}
- return createAnimation(view, 0, 1);
+ float startAlpha = 0;
+ if (startValues != null) {
+ startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+ if (startAlpha == 1) {
+ startAlpha = 0;
+ }
+ }
+ return createAnimation(view, startAlpha, 1);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
TransitionValues endValues) {
- return createAnimation(view, 1, 0);
+ float startAlpha = 1;
+ if (startValues != null) {
+ startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+ }
+ return createAnimation(view, startAlpha, 0);
}
private static class FadeAnimatorListener extends AnimatorListenerAdapter {
private final View mView;
- private float mPausedAlpha = -1;
private boolean mLayerTypeChanged = false;
public FadeAnimatorListener(View view) {
@@ -171,16 +187,5 @@
mView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
-
- @Override
- public void onAnimationPause(Animator animator) {
- mPausedAlpha = mView.getTransitionAlpha();
- mView.setTransitionAlpha(1);
- }
-
- @Override
- public void onAnimationResume(Animator animator) {
- mView.setTransitionAlpha(mPausedAlpha);
- }
}
}
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
new file mode 100644
index 0000000..8e66f86
--- /dev/null
+++ b/core/java/android/view/FrameMetrics.java
@@ -0,0 +1,281 @@
+/*
+ * 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 android.view;
+
+import android.annotation.IntDef;
+import android.view.Window;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class containing timing data for various milestones in a frame
+ * lifecycle reported by the rendering subsystem.
+ * <p>
+ * Supported metrics can be queried via their corresponding identifier.
+ * </p>
+ */
+public final class FrameMetrics {
+
+ /**
+ * Metric identifier for unknown delay.
+ * <p>
+ * Represents the number of nanoseconds elapsed waiting for the
+ * UI thread to become responsive and process the frame. This
+ * should be 0 most of the time.
+ * </p>
+ */
+ public static final int UNKNOWN_DELAY_DURATION = 0;
+
+ /**
+ * Metric identifier for input handling duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed issuing
+ * input handling callbacks.
+ * </p>
+ */
+ public static final int INPUT_HANDLING_DURATION = 1;
+
+ /**
+ * Metric identifier for animation callback duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed issuing
+ * animation callbacks.
+ * </p>
+ */
+ public static final int ANIMATION_DURATION = 2;
+
+ /**
+ * Metric identifier for layout/measure duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed measuring
+ * and laying out the invalidated pieces of the view hierarchy.
+ * </p>
+ */
+ public static final int LAYOUT_MEASURE_DURATION = 3;
+ /**
+ * Metric identifier for draw duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed computing
+ * DisplayLists for transformations applied to the view
+ * hierarchy.
+ * </p>
+ */
+ public static final int DRAW_DURATION = 4;
+
+ /**
+ * Metric identifier for sync duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed
+ * synchronizing the computed display lists with the render
+ * thread.
+ * </p>
+ */
+ public static final int SYNC_DURATION = 5;
+
+ /**
+ * Metric identifier for command issue duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed
+ * issuing draw commands to the GPU.
+ * </p>
+ */
+ public static final int COMMAND_ISSUE_DURATION = 6;
+
+ /**
+ * Metric identifier for swap buffers duration.
+ * <p>
+ * Represents the number of nanoseconds elapsed issuing
+ * the frame buffer for this frame to the display
+ * subsystem.
+ * </p>
+ */
+ public static final int SWAP_BUFFERS_DURATION = 7;
+
+ /**
+ * Metric identifier for total frame duration.
+ * <p>
+ * Represents the total time in nanoseconds this frame took to render
+ * and be issued to the display subsystem.
+ * </p>
+ * <p>
+ * Equal to the sum of the values of all other time-valued metric
+ * identifiers.
+ * </p>
+ */
+ public static final int TOTAL_DURATION = 8;
+
+ /**
+ * Metric identifier for a boolean value determining whether this frame was
+ * the first to draw in a new Window layout.
+ * <p>
+ * {@link #getMetric(int)} will return 0 for false, 1 for true.
+ * </p>
+ * <p>
+ * First draw frames are expected to be slow and should usually be exempt
+ * from display jank calculations as they do not cause skips in animations
+ * and are usually hidden by window animations or other tricks.
+ * </p>
+ */
+ public static final int FIRST_DRAW_FRAME = 9;
+
+ private static final int FRAME_INFO_FLAG_FIRST_DRAW = 1 << 0;
+
+ /**
+ * Identifiers for metrics available for each frame.
+ *
+ * {@see {@link #getMetric(int)}}
+ * @hide
+ */
+ @IntDef({
+ UNKNOWN_DELAY_DURATION,
+ INPUT_HANDLING_DURATION,
+ ANIMATION_DURATION,
+ LAYOUT_MEASURE_DURATION,
+ DRAW_DURATION,
+ SYNC_DURATION,
+ COMMAND_ISSUE_DURATION,
+ SWAP_BUFFERS_DURATION,
+ TOTAL_DURATION,
+ FIRST_DRAW_FRAME,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Metric {}
+
+ /**
+ * Timestamp indices for frame milestones.
+ *
+ * May change from release to release.
+ *
+ * Must be kept in sync with frameworks/base/libs/hwui/FrameInfo.h.
+ *
+ * @hide
+ */
+ @IntDef ({
+ Index.FLAGS,
+ Index.INTENDED_VSYNC,
+ Index.VSYNC,
+ Index.OLDEST_INPUT_EVENT,
+ Index.NEWEST_INPUT_EVENT,
+ Index.HANDLE_INPUT_START,
+ Index.ANIMATION_START,
+ Index.PERFORM_TRAVERSALS_START,
+ Index.DRAW_START,
+ Index.SYNC_QUEUED,
+ Index.SYNC_START,
+ Index.ISSUE_DRAW_COMMANDS_START,
+ Index.SWAP_BUFFERS,
+ Index.FRAME_COMPLETED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface Index {
+ int FLAGS = 0;
+ int INTENDED_VSYNC = 1;
+ int VSYNC = 2;
+ int OLDEST_INPUT_EVENT = 3;
+ int NEWEST_INPUT_EVENT = 4;
+ int HANDLE_INPUT_START = 5;
+ int ANIMATION_START = 6;
+ int PERFORM_TRAVERSALS_START = 7;
+ int DRAW_START = 8;
+ int SYNC_QUEUED = 9;
+ int SYNC_START = 10;
+ int ISSUE_DRAW_COMMANDS_START = 11;
+ int SWAP_BUFFERS = 12;
+ int FRAME_COMPLETED = 13;
+
+ int FRAME_STATS_COUNT = 14; // must always be last
+ }
+
+ /*
+ * Bucket endpoints for each Metric defined above.
+ *
+ * Each defined metric *must* have a corresponding entry
+ * in this list.
+ */
+ private static final int[] DURATIONS = new int[] {
+ // UNKNOWN_DELAY
+ Index.INTENDED_VSYNC, Index.HANDLE_INPUT_START,
+ // INPUT_HANDLING
+ Index.HANDLE_INPUT_START, Index.ANIMATION_START,
+ // ANIMATION
+ Index.ANIMATION_START, Index.PERFORM_TRAVERSALS_START,
+ // LAYOUT_MEASURE
+ Index.PERFORM_TRAVERSALS_START, Index.DRAW_START,
+ // DRAW
+ Index.DRAW_START, Index.SYNC_QUEUED,
+ // SYNC
+ Index.SYNC_START, Index.ISSUE_DRAW_COMMANDS_START,
+ // COMMAND_ISSUE
+ Index.ISSUE_DRAW_COMMANDS_START, Index.SWAP_BUFFERS,
+ // SWAP_BUFFERS
+ Index.SWAP_BUFFERS, Index.FRAME_COMPLETED,
+ // TOTAL_DURATION
+ Index.INTENDED_VSYNC, Index.FRAME_COMPLETED,
+ };
+
+ /* package */ final long[] mTimingData;
+
+ /**
+ * Constructs a FrameMetrics object as a copy.
+ * <p>
+ * Use this method to copy out metrics reported by
+ * {@link Window.FrameMetricsListener#onMetricsAvailable(Window, FrameMetrics, int)}
+ * </p>
+ * @param other the FrameMetrics object to copy.
+ */
+ public FrameMetrics(FrameMetrics other) {
+ mTimingData = new long[Index.FRAME_STATS_COUNT];
+ System.arraycopy(other.mTimingData, 0, mTimingData, 0, mTimingData.length);
+ }
+
+ /**
+ * @hide
+ */
+ FrameMetrics() {
+ mTimingData = new long[Index.FRAME_STATS_COUNT];
+ }
+
+ /**
+ * Retrieves the value associated with Metric identifier {@code id}
+ * for this frame.
+ * <p>
+ * Boolean metrics are represented in [0,1], with 0 corresponding to
+ * false, and 1 corresponding to true.
+ * </p>
+ * @param id the metric to retrieve
+ * @return the value of the metric or -1 if it is not available.
+ */
+ public long getMetric(@Metric int id) {
+ if (id < UNKNOWN_DELAY_DURATION || id > FIRST_DRAW_FRAME) {
+ return -1;
+ }
+
+ if (mTimingData == null) {
+ return -1;
+ }
+
+ if (id == FIRST_DRAW_FRAME) {
+ return (mTimingData[Index.FLAGS] & FRAME_INFO_FLAG_FIRST_DRAW) != 0 ? 1 : 0;
+ }
+
+ int durationsIdx = 2 * id;
+ return mTimingData[DURATIONS[durationsIdx + 1]]
+ - mTimingData[DURATIONS[durationsIdx]];
+ }
+}
+
diff --git a/core/java/android/view/FrameMetricsObserver.java b/core/java/android/view/FrameMetricsObserver.java
new file mode 100644
index 0000000..f38f8b7
--- /dev/null
+++ b/core/java/android/view/FrameMetricsObserver.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.view;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import android.os.Looper;
+import android.os.MessageQueue;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.lang.NullPointerException;
+import java.lang.ref.WeakReference;
+import java.lang.SuppressWarnings;
+
+/**
+ * Provides streaming access to frame stats information from the rendering
+ * subsystem to apps.
+ *
+ * @hide
+ */
+public class FrameMetricsObserver {
+ private MessageQueue mMessageQueue;
+
+ private WeakReference<Window> mWindow;
+
+ private FrameMetrics mFrameMetrics;
+
+ /* package */ Window.FrameMetricsListener mListener;
+ /* package */ VirtualRefBasePtr mNative;
+
+ /**
+ * Creates a FrameMetricsObserver
+ *
+ * @param looper the looper to use when invoking callbacks
+ */
+ FrameMetricsObserver(@NonNull Window window, @NonNull Looper looper,
+ @NonNull Window.FrameMetricsListener listener) {
+ if (looper == null) {
+ throw new NullPointerException("looper cannot be null");
+ }
+
+ mMessageQueue = looper.getQueue();
+ if (mMessageQueue == null) {
+ throw new IllegalStateException("invalid looper, null message queue\n");
+ }
+
+ mFrameMetrics = new FrameMetrics();
+ mWindow = new WeakReference<>(window);
+ mListener = listener;
+ }
+
+ // Called by native on the provided Handler
+ @SuppressWarnings("unused")
+ private void notifyDataAvailable(int dropCount) {
+ final Window window = mWindow.get();
+ if (window != null) {
+ mListener.onMetricsAvailable(window, mFrameMetrics, dropCount);
+ }
+ }
+}
diff --git a/core/java/android/view/FrameStatsObserver.java b/core/java/android/view/FrameStatsObserver.java
deleted file mode 100644
index 0add607..0000000
--- a/core/java/android/view/FrameStatsObserver.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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 android.view;
-
-import android.annotation.NonNull;
-import android.util.Log;
-import android.os.Looper;
-import android.os.MessageQueue;
-
-import com.android.internal.util.VirtualRefBasePtr;
-
-import java.lang.NullPointerException;
-import java.lang.ref.WeakReference;
-import java.lang.SuppressWarnings;
-
-/**
- * Provides streaming access to frame stats information from the rendering
- * subsystem to apps.
- *
- * @hide
- */
-public abstract class FrameStatsObserver {
- private static final String TAG = "FrameStatsObserver";
-
- private MessageQueue mMessageQueue;
- private long[] mBuffer;
-
- private FrameStats mFrameStats;
-
- /* package */ ThreadedRenderer mRenderer;
- /* package */ VirtualRefBasePtr mNative;
-
- /**
- * Containing class for frame statistics reported
- * by the rendering subsystem.
- */
- public static class FrameStats {
- /**
- * Precise timing data for various milestones in a frame
- * lifecycle.
- *
- * This data is exactly the same as what is returned by
- * `adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats`
- *
- * The fields reported may change from release to release.
- *
- * @see {@link http://developer.android.com/training/testing/performance.html}
- * for a description of the fields present.
- */
- public long[] mTimingData;
- }
-
- /**
- * Creates a FrameStatsObserver
- *
- * @param looper the looper to use when invoking callbacks
- */
- public FrameStatsObserver(@NonNull Looper looper) {
- if (looper == null) {
- throw new NullPointerException("looper cannot be null");
- }
-
- mMessageQueue = looper.getQueue();
- if (mMessageQueue == null) {
- throw new IllegalStateException("invalid looper, null message queue\n");
- }
-
- mFrameStats = new FrameStats();
- }
-
- /**
- * Called on provided looper when frame stats data is available
- * for the previous frame.
- *
- * Clients of this class must do as little work as possible within
- * this callback, as the buffer is shared between the producer and consumer.
- *
- * If the consumer is still executing within this method when there is new
- * data available that data will be dropped. The producer cannot
- * wait on the consumer.
- *
- * @param data the newly available data
- */
- public abstract void onDataAvailable(FrameStats data);
-
- /**
- * Returns the number of reports dropped as a result of a slow
- * consumer.
- */
- public long getDroppedReportCount() {
- if (mRenderer == null) {
- return 0;
- }
-
- return mRenderer.getDroppedFrameReportCount();
- }
-
- public boolean isRegistered() {
- return mRenderer != null && mNative != null;
- }
-
- // === called by native === //
- @SuppressWarnings("unused")
- private void notifyDataAvailable() {
- mFrameStats.mTimingData = mBuffer;
- onDataAvailable(mFrameStats);
- }
-}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1740f07..5b9930b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -166,6 +166,7 @@
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
void setAppVisibility(IBinder token, boolean visible);
+ void notifyAppStopped(IBinder token);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 8b06ecf..ca41d78 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -354,8 +354,6 @@
private boolean mEnabled;
private boolean mRequested = true;
- private HashSet<FrameStatsObserver> mFrameStatsObservers;
-
ThreadedRenderer(Context context, boolean translucent) {
final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -964,29 +962,14 @@
}
}
- void addFrameStatsObserver(FrameStatsObserver fso) {
- if (mFrameStatsObservers == null) {
- mFrameStatsObservers = new HashSet<>();
- }
-
- long nativeFso = nAddFrameStatsObserver(mNativeProxy, fso);
- fso.mRenderer = this;
- fso.mNative = new VirtualRefBasePtr(nativeFso);
- mFrameStatsObservers.add(fso);
+ void addFrameMetricsObserver(FrameMetricsObserver observer) {
+ long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer);
+ observer.mNative = new VirtualRefBasePtr(nativeObserver);
}
- void removeFrameStatsObserver(FrameStatsObserver fso) {
- if (!mFrameStatsObservers.remove(fso)) {
- throw new IllegalArgumentException("attempt to remove FrameStatsObserver that was never added");
- }
-
- nRemoveFrameStatsObserver(mNativeProxy, fso.mNative.get());
- fso.mRenderer = null;
- fso.mNative = null;
- }
-
- long getDroppedFrameReportCount() {
- return nGetDroppedFrameReportCount(mNativeProxy);
+ void removeFrameMetricsObserver(FrameMetricsObserver observer) {
+ nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get());
+ observer.mNative = null;
}
static native void setupShadersDiskCache(String cacheFile);
@@ -1044,7 +1027,6 @@
private static native void nSetContentDrawBounds(long nativeProxy, int left,
int top, int right, int bottom);
- private static native long nAddFrameStatsObserver(long nativeProxy, FrameStatsObserver fso);
- private static native void nRemoveFrameStatsObserver(long nativeProxy, long nativeFso);
- private static native long nGetDroppedFrameReportCount(long nativeProxy);
+ private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
+ private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 127157b..2612ab2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3703,9 +3703,9 @@
private ViewPropertyAnimator mAnimator = null;
/**
- * List of FrameStatsObservers pending registration when mAttachInfo is null.
+ * List of registered FrameMetricsObservers.
*/
- private ArrayList<FrameStatsObserver> mPendingFrameStatsObservers;
+ private ArrayList<FrameMetricsObserver> mFrameMetricsObservers;
/**
* Flag indicating that a drag can cross window boundaries. When
@@ -5479,19 +5479,29 @@
*
* @hide
*/
- public void addFrameStatsObserver(FrameStatsObserver fso) {
+ public void addFrameMetricsListener(Window window, Window.FrameMetricsListener listener,
+ Handler handler) {
if (mAttachInfo != null) {
if (mAttachInfo.mHardwareRenderer != null) {
- mAttachInfo.mHardwareRenderer.addFrameStatsObserver(fso);
+ if (mFrameMetricsObservers == null) {
+ mFrameMetricsObservers = new ArrayList<>();
+ }
+
+ FrameMetricsObserver fmo = new FrameMetricsObserver(window,
+ handler.getLooper(), listener);
+ mFrameMetricsObservers.add(fmo);
+ mAttachInfo.mHardwareRenderer.addFrameMetricsObserver(fmo);
} else {
Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats");
}
} else {
- if (mPendingFrameStatsObservers == null) {
- mPendingFrameStatsObservers = new ArrayList<>();
+ if (mFrameMetricsObservers == null) {
+ mFrameMetricsObservers = new ArrayList<>();
}
- mPendingFrameStatsObservers.add(fso);
+ FrameMetricsObserver fmo = new FrameMetricsObserver(window,
+ handler.getLooper(), listener);
+ mFrameMetricsObservers.add(fmo);
}
}
@@ -5500,32 +5510,45 @@
*
* @hide
*/
- public void removeFrameStatsObserver(FrameStatsObserver fso) {
+ public void removeFrameMetricsListener(Window.FrameMetricsListener listener) {
ThreadedRenderer renderer = getHardwareRenderer();
-
- if (mPendingFrameStatsObservers != null) {
- mPendingFrameStatsObservers.remove(fso);
+ FrameMetricsObserver fmo = findFrameMetricsObserver(listener);
+ if (fmo == null) {
+ throw new IllegalArgumentException("attempt to remove FrameMetricsListener that was never added");
}
- if (renderer != null) {
- renderer.removeFrameStatsObserver(fso);
+ if (mFrameMetricsObservers != null) {
+ mFrameMetricsObservers.remove(fmo);
+ if (renderer != null) {
+ renderer.removeFrameMetricsObserver(fmo);
+ }
}
}
- private void registerPendingFrameStatsObservers() {
- if (mPendingFrameStatsObservers != null) {
+ private void registerPendingFrameMetricsObservers() {
+ if (mFrameMetricsObservers != null) {
ThreadedRenderer renderer = getHardwareRenderer();
if (renderer != null) {
- for (FrameStatsObserver fso : mPendingFrameStatsObservers) {
- renderer.addFrameStatsObserver(fso);
+ for (FrameMetricsObserver fmo : mFrameMetricsObservers) {
+ renderer.addFrameMetricsObserver(fmo);
}
} else {
Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats");
}
- mPendingFrameStatsObservers = null;
}
}
+ private FrameMetricsObserver findFrameMetricsObserver(Window.FrameMetricsListener listener) {
+ for (int i = 0; i < mFrameMetricsObservers.size(); i++) {
+ FrameMetricsObserver observer = mFrameMetricsObservers.get(i);
+ if (observer.mListener == listener) {
+ return observer;
+ }
+ }
+
+ return null;
+ }
+
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
@@ -15160,7 +15183,7 @@
mFloatingTreeObserver = null;
}
- registerPendingFrameStatsObservers();
+ registerPendingFrameMetricsObservers();
if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
mAttachInfo.mScrollContainers.add(this);
@@ -16582,11 +16605,12 @@
RenderNode renderNode = null;
Bitmap cache = null;
int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
- if (layerType == LAYER_TYPE_SOFTWARE
- || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) {
- // If not drawing with RenderNode, treat HW layers as SW
- layerType = LAYER_TYPE_SOFTWARE;
- buildDrawingCache(true);
+ if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {
+ if (layerType != LAYER_TYPE_NONE) {
+ // If not drawing with RenderNode, treat HW layers as SW
+ layerType = LAYER_TYPE_SOFTWARE;
+ buildDrawingCache(true);
+ }
cache = getDrawingCache(true);
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c68a740..9f05990 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -34,6 +34,7 @@
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -604,6 +605,34 @@
void onRestrictedCaptionAreaChanged(Rect rect);
}
+ /**
+ * Callback for clients that want frame timing information for each
+ * frame rendered by the Window.
+ */
+ public interface FrameMetricsListener {
+ /**
+ * Called when information is available for the previously rendered frame.
+ *
+ * Reports can be dropped if this callback takes too
+ * long to execute, as the report producer cannot wait for the consumer to
+ * complete.
+ *
+ * It is highly recommended that clients copy the passed in FrameMetrics
+ * via {@link FrameMetrics#FrameMetrics(FrameMetrics)} within this method and defer
+ * additional computation or storage to another thread to avoid unnecessarily
+ * dropping reports.
+ *
+ * @param window The {@link Window} on which the frame was displayed.
+ * @param frameMetrics the available metrics. This object is reused on every call
+ * and thus <strong>this reference is not valid outside the scope of this method</strong>.
+ * @param dropCountSinceLastInvocation the number of reports dropped since the last time
+ * this callback was invoked.
+ */
+ void onMetricsAvailable(Window window, FrameMetrics frameMetrics,
+ int dropCountSinceLastInvocation);
+ }
+
+
public Window(Context context) {
mContext = context;
mFeatures = mLocalFeatures = getDefaultFeatures(context);
@@ -798,33 +827,28 @@
* Set an observer to collect frame stats for each frame rendererd in this window.
*
* Must be in hardware rendering mode.
- * @hide
*/
- public final void addFrameStatsObserver(@NonNull FrameStatsObserver fso) {
+ public final void addFrameMetricsListener(@NonNull FrameMetricsListener listener,
+ Handler handler) {
final View decorView = getDecorView();
if (decorView == null) {
throw new IllegalStateException("can't observe a Window without an attached view");
}
- if (fso == null) {
- throw new NullPointerException("FrameStatsObserver cannot be null");
+ if (listener == null) {
+ throw new NullPointerException("listener cannot be null");
}
- if (fso.isRegistered()) {
- throw new IllegalStateException("FrameStatsObserver already registered on a Window.");
- }
-
- decorView.addFrameStatsObserver(fso);
+ decorView.addFrameMetricsListener(this, listener, handler);
}
/**
* Remove observer and stop listening to frame stats for this window.
- * @hide
*/
- public final void removeFrameStatsObserver(FrameStatsObserver fso) {
+ public final void removeFrameMetricsListener(FrameMetricsListener listener) {
final View decorView = getDecorView();
if (decorView != null) {
- getDecorView().removeFrameStatsObserver(fso);
+ getDecorView().removeFrameMetricsListener(listener);
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 6e38b32..947906b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -460,6 +460,11 @@
/** Unregister a system listener for touch events */
void unregisterPointerEventListener(PointerEventListener listener);
+
+ /**
+ * @return The content insets of the docked divider window.
+ */
+ int getDockedDividerInsetsLw();
}
public interface PointerEventListener {
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 43306d0..d97f8af 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -38,7 +38,6 @@
import android.util.Slog;
import android.util.Xml;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import android.view.inputmethod.InputMethodSubtypeArray;
import java.io.IOException;
import java.util.ArrayList;
@@ -122,7 +121,7 @@
* @param context The Context in which we are parsing the input method.
* @param service The ResolveInfo returned from the package manager about
* this input method's component.
- * @param additionalSubtypes additional subtypes being added to this InputMethodInfo
+ * @param additionalSubtypesMap additional subtypes being added to this InputMethodInfo
* @hide
*/
public InputMethodInfo(Context context, ResolveInfo service,
@@ -429,6 +428,18 @@
}
}
+ /**
+ * @return {@code true} if the {@link android.inputmethodservice.InputMethodService} is marked
+ * to be Encryption-Aware.
+ * @hide
+ */
+ public boolean isEncryptionAware() {
+ if (mService == null || mService.serviceInfo == null) {
+ return false;
+ }
+ return mService.serviceInfo.encryptionAware;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index e31bbe9..1d242d3 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -65,6 +65,11 @@
}
@Override
+ public boolean getFreezesText() {
+ return true;
+ }
+
+ @Override
protected boolean getDefaultEditable() {
return true;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3a61fcd..1826dd8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1851,8 +1851,7 @@
updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped));
if (mCursorCount == 2) {
- updateCursorPosition(1, middle, bottom,
- layout.getSecondaryHorizontal(offset, clamped));
+ updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped));
}
}
@@ -2151,21 +2150,60 @@
return mSelectionModifierCursorController;
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public Drawable[] getCursorDrawable() {
+ return mCursorDrawable;
+ }
+
private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
if (mCursorDrawable[cursorIndex] == null)
mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable(
mTextView.mCursorDrawableRes);
-
- if (mTempRect == null) mTempRect = new Rect();
- mCursorDrawable[cursorIndex].getPadding(mTempRect);
- final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
- horizontal = Math.max(0.5f, horizontal - 0.5f);
- final int left = (int) (horizontal) - mTempRect.left;
- mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+ final Drawable drawable = mCursorDrawable[cursorIndex];
+ final int left = clampCursorHorizontalPosition(drawable, horizontal);
+ final int width = drawable.getIntrinsicWidth();
+ drawable.setBounds(left, top - mTempRect.top, left + width,
bottom + mTempRect.bottom);
}
/**
+ * Return clamped position for the cursor. If the cursor is within the boundaries of the view,
+ * then it is offset with the left padding of the cursor drawable. If the cursor is at
+ * the beginning or the end of the text then its drawable edge is aligned with left or right of
+ * the view boundary.
+ *
+ * @param drawable Cursor drawable.
+ * @param horizontal Horizontal position for the cursor.
+ * @return The clamped horizontal position for the cursor.
+ */
+ private final int clampCursorHorizontalPosition(final Drawable drawable, float
+ horizontal) {
+ horizontal = Math.max(0.5f, horizontal - 0.5f);
+ if (mTempRect == null) mTempRect = new Rect();
+ drawable.getPadding(mTempRect);
+ int scrollX = mTextView.getScrollX();
+ float horizontalDiff = horizontal - scrollX;
+ int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft()
+ - mTextView.getCompoundPaddingRight();
+
+ final int left;
+ if (horizontalDiff >= (viewClippedWidth - 1f)) {
+ // at the rightmost position
+ final int cursorWidth = drawable.getIntrinsicWidth();
+ left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right);
+ } else if (Math.abs(horizontalDiff) <= 1f) {
+ // at the leftmost position
+ left = scrollX - mTempRect.left;
+ } else {
+ left = (int) horizontal - mTempRect.left;
+ }
+ return left;
+ }
+
+ /**
* Called by the framework in response to a text auto-correction (such as fixing a typo using a
* a dictionary) from the current input method, provided by it calling
* {@link InputConnection#commitCorrection} InputConnection.commitCorrection()}. The default
@@ -3919,8 +3957,8 @@
final Layout layout = mTextView.getLayout();
if (layout != null && oldDrawable != mDrawable && isShowing()) {
// Update popup window position.
- mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
- getHorizontalOffset() + getCursorOffset());
+ mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
+ getHorizontalOffset() + getCursorOffset();
mPositionX += mTextView.viewportToContentHorizontalOffset();
mPositionHasChanged = true;
updatePosition(mLastParentX, mLastParentY, false, false);
@@ -4049,8 +4087,8 @@
final int line = layout.getLineForOffset(offset);
mPrevLine = line;
- mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
- getHorizontalOffset() + getCursorOffset());
+ mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX -
+ getHorizontalOffset() + getCursorOffset();
mPositionY = layout.getLineBottom(line);
// Take TextView's padding and scroll into account.
@@ -4062,6 +4100,17 @@
}
}
+ /**
+ * Return the clamped horizontal position for the first cursor.
+ *
+ * @param layout Text layout.
+ * @param offset Character offset for the cursor.
+ * @return The clamped horizontal position for the cursor.
+ */
+ int getCursorHorizontalPosition(Layout layout, int offset) {
+ return (int) (layout.getPrimaryHorizontal(offset) - 0.5f);
+ }
+
public void updatePosition(int parentPositionX, int parentPositionY,
boolean parentPositionChanged, boolean parentScrolled) {
positionAtCursorOffset(getCurrentCursorOffset(), parentScrolled);
@@ -4300,6 +4349,16 @@
}
@Override
+ int getCursorHorizontalPosition(Layout layout, int offset) {
+ final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null;
+ if (drawable != null) {
+ final float horizontal = layout.getPrimaryHorizontal(offset);
+ return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left;
+ }
+ return super.getCursorHorizontalPosition(layout, offset);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
final boolean result = super.onTouchEvent(ev);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 712a04b..a5c1da9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4101,36 +4101,42 @@
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
- boolean save = mFreezesText;
- int start = 0;
- int end = 0;
+ final boolean freezesText = getFreezesText();
+ boolean hasSelection = false;
+ int start = -1;
+ int end = -1;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
- save = true;
+ hasSelection = true;
}
}
- if (save) {
+ if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
- // XXX Should also save the current scroll position!
- ss.selStart = start;
- ss.selEnd = end;
- if (mText instanceof Spanned) {
- Spannable sp = new SpannableStringBuilder(mText);
+ if (freezesText) {
+ if (mText instanceof Spanned) {
+ final Spannable sp = new SpannableStringBuilder(mText);
- if (mEditor != null) {
- removeMisspelledSpans(sp);
- sp.removeSpan(mEditor.mSuggestionRangeSpan);
+ if (mEditor != null) {
+ removeMisspelledSpans(sp);
+ sp.removeSpan(mEditor.mSuggestionRangeSpan);
+ }
+
+ ss.text = sp;
+ } else {
+ ss.text = mText.toString();
}
+ }
- ss.text = sp;
- } else {
- ss.text = mText.toString();
+ if (hasSelection) {
+ // XXX Should also save the current scroll position!
+ ss.selStart = start;
+ ss.selEnd = end;
}
if (isFocused() && start >= 0 && end >= 0) {
@@ -4224,7 +4230,9 @@
* position. By default this is false, not saving the text. Set to true
* if the text in the text view is not being saved somewhere else in
* persistent storage (such as in a content provider) so that if the
- * view is later thawed the user will not lose their data.
+ * view is later thawed the user will not lose their data. For
+ * {@link android.widget.EditText} it is always enabled, regardless of
+ * the value of the attribute.
*
* @param freezesText Controls whether a frozen icicle should include the
* entire text data: true to include it, false to not.
@@ -4238,7 +4246,7 @@
/**
* Return whether this text view is including its entire text contents
- * in frozen icicles.
+ * in frozen icicles. For {@link android.widget.EditText} it always returns true.
*
* @return Returns true if text is included, false if it isn't.
*
@@ -5452,15 +5460,9 @@
return (int) Math.max(0, mShadowDy + mShadowRadius);
}
- private int getFudgedPaddingRight() {
- // Add sufficient space for cursor and tone marks
- int cursorWidth = 2 + (int)mTextPaint.density; // adequate for Material cursors
- return Math.max(0, getCompoundPaddingRight() - (cursorWidth - 1));
- }
-
@Override
protected int getRightPaddingOffset() {
- return -(getFudgedPaddingRight() - mPaddingRight) +
+ return -(getCompoundPaddingRight() - mPaddingRight) +
(int) Math.max(0, mShadowDx + mShadowRadius);
}
@@ -5805,7 +5807,7 @@
float clipLeft = compoundPaddingLeft + scrollX;
float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
- float clipRight = right - left - getFudgedPaddingRight() + scrollX;
+ float clipRight = right - left - getCompoundPaddingRight() + scrollX;
float clipBottom = bottom - top + scrollY -
((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
@@ -10111,8 +10113,8 @@
* {@link View#onSaveInstanceState}.
*/
public static class SavedState extends BaseSavedState {
- int selStart;
- int selEnd;
+ int selStart = -1;
+ int selEnd = -1;
CharSequence text;
boolean frozenWithFocus;
CharSequence error;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index d4ada95..b2ae835 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1706,7 +1706,9 @@
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
- addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ // Put it below the color views.
+ addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 597c522..84d0fc7 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -136,8 +136,7 @@
/ (mFirstSplitTarget.position - getStartInset());
} else if (position > mLastSplitTarget.position) {
return (float) (position - mLastSplitTarget.position)
- / (mDismissEndTarget.position - getEndInset()
- - mLastSplitTarget.position - mDividerSize);
+ / (mDismissEndTarget.position - mLastSplitTarget.position - mDividerSize);
}
return 0f;
}
@@ -222,7 +221,8 @@
addMiddleTarget(isHorizontalDivision);
break;
}
- mTargets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));
+ int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right;
+ mTargets.add(new SnapTarget(dividerMax - navBarSize, SnapTarget.FLAG_DISMISS_END, 0.35f));
}
private void addFixedDivisionTargets(boolean isHorizontalDivision) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index acd0501..dd0e456 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -41,6 +41,7 @@
#include <Animator.h>
#include <AnimationContext.h>
#include <FrameInfo.h>
+#include <FrameMetricsObserver.h>
#include <IContextFactory.h>
#include <JankTracker.h>
#include <RenderNode.h>
@@ -56,10 +57,11 @@
using namespace android::uirenderer::renderthread;
struct {
- jfieldID buffer;
+ jfieldID frameMetrics;
+ jfieldID timingDataBuffer;
jfieldID messageQueue;
- jmethodID notifyData;
-} gFrameStatsObserverClassInfo;
+ jmethodID callback;
+} gFrameMetricsObserverClassInfo;
static JNIEnv* getenv(JavaVM* vm) {
JNIEnv* env;
@@ -239,31 +241,46 @@
mBuffer = buffer;
}
+ void setDropCount(int dropCount) {
+ mDropCount = dropCount;
+ }
+
virtual void handleMessage(const Message& message);
private:
JavaVM* mVm;
sp<ObserverProxy> mObserver;
- BufferPool::Buffer* mBuffer;
+ BufferPool::Buffer* mBuffer = nullptr;
+ int mDropCount = 0;
};
-class ObserverProxy : public FrameStatsObserver {
+static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
+ jobject frameMetrics = env->GetObjectField(
+ observer, gFrameMetricsObserverClassInfo.frameMetrics);
+ LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object");
+ jobject buffer = env->GetObjectField(
+ frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer);
+ LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer");
+ return reinterpret_cast<jlongArray>(buffer);
+}
+
+class ObserverProxy : public FrameMetricsObserver {
public:
- ObserverProxy(JavaVM *vm, jobject fso) : mVm(vm) {
+ ObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
JNIEnv* env = getenv(mVm);
- jlongArray longArrayLocal = env->NewLongArray(kBufferSize);
- LOG_ALWAYS_FATAL_IF(longArrayLocal == nullptr,
- "OOM: can't allocate frame stats buffer");
- env->SetObjectField(fso, gFrameStatsObserverClassInfo.buffer, longArrayLocal);
-
- mFsoWeak = env->NewWeakGlobalRef(fso);
- LOG_ALWAYS_FATAL_IF(mFsoWeak == nullptr,
+ mObserverWeak = env->NewWeakGlobalRef(observer);
+ LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
"unable to create frame stats observer reference");
- jobject messageQueueLocal =
- env->GetObjectField(fso, gFrameStatsObserverClassInfo.messageQueue);
+ jlongArray buffer = get_metrics_buffer(env, observer);
+ jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer));
+ LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize,
+ "Mismatched Java/Native FrameMetrics data format.");
+
+ jobject messageQueueLocal = env->GetObjectField(
+ observer, gFrameMetricsObserverClassInfo.messageQueue);
mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");
@@ -274,17 +291,18 @@
~ObserverProxy() {
JNIEnv* env = getenv(mVm);
- env->DeleteWeakGlobalRef(mFsoWeak);
+ env->DeleteWeakGlobalRef(mObserverWeak);
}
- jweak getJavaObjectRef() {
- return mFsoWeak;
+ jweak getObserverReference() {
+ return mObserverWeak;
}
- virtual void notify(BufferPool::Buffer* buffer) {
+ virtual void notify(BufferPool::Buffer* buffer, int dropCount) {
buffer->incRef();
mMessageHandler->setBuffer(buffer);
mMessageHandler->setObserver(this);
+ mMessageHandler->setDropCount(dropCount);
mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
}
@@ -292,26 +310,27 @@
static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes);
JavaVM* mVm;
- jweak mFsoWeak;
+ jweak mObserverWeak;
+ jobject mJavaBufferGlobal;
sp<MessageQueue> mMessageQueue;
sp<NotifyHandler> mMessageHandler;
Message mMessage;
+
};
void NotifyHandler::handleMessage(const Message& message) {
JNIEnv* env = getenv(mVm);
- jobject target = env->NewLocalRef(mObserver->getJavaObjectRef());
+ jobject target = env->NewLocalRef(mObserver->getObserverReference());
if (target != nullptr) {
- jobject javaBuffer = env->GetObjectField(target, gFrameStatsObserverClassInfo.buffer);
- if (javaBuffer != nullptr) {
- env->SetLongArrayRegion(reinterpret_cast<jlongArray>(javaBuffer),
- 0, mBuffer->getSize(), mBuffer->getBuffer());
- env->CallVoidMethod(target, gFrameStatsObserverClassInfo.notifyData);
- env->DeleteLocalRef(target);
- }
+ jlongArray javaBuffer = get_metrics_buffer(env, target);
+ env->SetLongArrayRegion(javaBuffer,
+ 0, mBuffer->getSize(), mBuffer->getBuffer());
+ env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback,
+ mDropCount);
+ env->DeleteLocalRef(target);
}
mBuffer->release();
@@ -579,10 +598,10 @@
}
// ----------------------------------------------------------------------------
-// FrameStatsObserver
+// FrameMetricsObserver
// ----------------------------------------------------------------------------
-static jlong android_view_ThreadedRenderer_addFrameStatsObserver(JNIEnv* env,
+static jlong android_view_ThreadedRenderer_addFrameMetricsObserver(JNIEnv* env,
jclass clazz, jlong proxyPtr, jobject fso) {
JavaVM* vm = nullptr;
if (env->GetJavaVM(&vm) != JNI_OK) {
@@ -593,25 +612,18 @@
renderthread::RenderProxy* renderProxy =
reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
- FrameStatsObserver* observer = new ObserverProxy(vm, fso);
- renderProxy->addFrameStatsObserver(observer);
+ FrameMetricsObserver* observer = new ObserverProxy(vm, fso);
+ renderProxy->addFrameMetricsObserver(observer);
return reinterpret_cast<jlong>(observer);
}
-static void android_view_ThreadedRenderer_removeFrameStatsObserver(JNIEnv* env, jclass clazz,
+static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env, jclass clazz,
jlong proxyPtr, jlong observerPtr) {
- FrameStatsObserver* observer = reinterpret_cast<FrameStatsObserver*>(observerPtr);
+ FrameMetricsObserver* observer = reinterpret_cast<FrameMetricsObserver*>(observerPtr);
renderthread::RenderProxy* renderProxy =
reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
- renderProxy->removeFrameStatsObserver(observer);
-}
-
-static jint android_view_ThreadedRenderer_getDroppedFrameReportCount(JNIEnv* env, jclass clazz,
- jlong proxyPtr) {
- renderthread::RenderProxy* renderProxy =
- reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
- return renderProxy->getDroppedFrameReportCount();
+ renderProxy->removeFrameMetricsObserver(observer);
}
// ----------------------------------------------------------------------------
@@ -684,25 +696,26 @@
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
{ "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
- { "nAddFrameStatsObserver",
- "(JLandroid/view/FrameStatsObserver;)J",
- (void*)android_view_ThreadedRenderer_addFrameStatsObserver },
- { "nRemoveFrameStatsObserver",
+ { "nAddFrameMetricsObserver",
+ "(JLandroid/view/FrameMetricsObserver;)J",
+ (void*)android_view_ThreadedRenderer_addFrameMetricsObserver },
+ { "nRemoveFrameMetricsObserver",
"(JJ)V",
- (void*)android_view_ThreadedRenderer_removeFrameStatsObserver },
- { "nGetDroppedFrameReportCount",
- "(J)J",
- (void*)android_view_ThreadedRenderer_getDroppedFrameReportCount },
+ (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
- jclass clazz = FindClassOrDie(env, "android/view/FrameStatsObserver");
- gFrameStatsObserverClassInfo.messageQueue =
- GetFieldIDOrDie(env, clazz, "mMessageQueue", "Landroid/os/MessageQueue;");
- gFrameStatsObserverClassInfo.buffer =
- GetFieldIDOrDie(env, clazz, "mBuffer", "[J");
- gFrameStatsObserverClassInfo.notifyData =
- GetMethodIDOrDie(env, clazz, "notifyDataAvailable", "()V");
+ jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
+ gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
+ env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
+ gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie(
+ env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;");
+ gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie(
+ env, observerClass, "notifyDataAvailable", "(I)V");
+
+ jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics");
+ gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
+ env, metricsClass, "mTimingData", "[J");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4cddb6c..99daab4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -425,6 +425,8 @@
<protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -968,8 +970,7 @@
<!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
to record or display them to the user.
- <p>Not for use by third-party applications.
- @hide Pending API council approval -->
+ <p>Not for use by third-party applications. -->
<permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
android:protectionLevel="signature|privileged" />
@@ -2884,6 +2885,18 @@
<permission android:name="android.permission.DISPATCH_PROVISIONING_MESSAGE"
android:protectionLevel="signature|privileged" />
+ <!-- Allows the holder to read blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @hide -->
+ <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the holder to write blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @hide -->
+ <permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml b/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml
index 2dd0540..62af834 100644
--- a/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml
+++ b/core/res/res/drawable/ic_arrow_drop_right_black_24dp.xml
@@ -15,10 +15,10 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="25.0dp"
- android:viewportHeight="25.0"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
android:viewportWidth="24.0"
- android:width="25.0dp"
+ android:width="24.0dp"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
@@ -26,9 +26,8 @@
android:name="arrow"
android:rotation="90.0"
android:pivotX="12.0"
- android:pivotY="13.0"
- android:translateY="1.0">
+ android:pivotY="12.0">
<path android:fillColor="#000000" android:pathData="M7,14 L12,9 L17,14 L7,14 Z" />
<path android:pathData="M0,0 L24,0 L24,24 L0,24 L0,0 Z" />
</group>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/core/res/res/values-mcc310-mnc160-af/strings.xml b/core/res/res/values-mcc310-mnc160-af/strings.xml
new file mode 100644
index 0000000..72ca2f2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-af/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Om oproepe te maak en boodskappe oor Wi-Fi te stuur, vra jou diensverskaffer eers om hierdie diens op te stel. Skakel Wi-Fi-oproepe dan weer in Instellings aan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registreer by jou diensverskaffer"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi-oproep"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-am/strings.xml b/core/res/res/values-mcc310-mnc160-am/strings.xml
new file mode 100644
index 0000000..5a6635c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-am/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"በWi-Fi ላይ ጥሪዎችን ለማድረግ እና መልዕክቶችን ለመላክ መጀመሪያ የአገልግሎት አቅራቢዎ ይህን አገልግሎት እንዲያዘጋጅልዎ ይጠይቁ። ከዚያ ከቅንብሮች ሆነው እንደገና የWi-Fi ጥሪን ያብሩ።"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"የአገልግሎት አቅራቢዎ ጋር ይመዝገቡ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"የ%s Wi-Fi ጥሪ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ar/strings.xml b/core/res/res/values-mcc310-mnc160-ar/strings.xml
new file mode 100644
index 0000000..c0e4229
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ar/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"لإجراء مكالمات وإرسال رسائل عبر Wi-Fi، اطلب من مشغّل شبكة الجوّال أولاً إعداد هذا الجهاز، ثم شغّل الاتصال عبر Wi-Fi مرة أخرى من خلال الإعدادات."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"التسجيل لدى مشغّل شبكة الجوّال"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s جارٍ الاتصال عبر Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-az-rAZ/strings.xml b/core/res/res/values-mcc310-mnc160-az-rAZ/strings.xml
new file mode 100644
index 0000000..0c250cd
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-az-rAZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi üzərindən zəng etmək və mesaj göndərmək üçün ilk öncə operatordan bu xidməti ayarlamağı tələb edin. Sonra Ayarlardan Wi-Fi çağrısını aktivləşdirin."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Operatorla qeydiyyatdan keçin"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi Zəngi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc160-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..d351d5e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-b+sr+Latn/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Da biste upućivali pozive i slali poruke preko Wi-Fi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko Wi-Fi-ja."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrujte se kod mobilnog operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Wi-Fi pozivanje preko operatera %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-bg/strings.xml b/core/res/res/values-mcc310-mnc160-bg/strings.xml
new file mode 100644
index 0000000..88715f5
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-bg/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"За да извършвате обаждания и да изпращате съобщения през Wi-Fi, първо помолете оператора си да настрои тази услуга. След това включете отново функцията за обаждания през Wi-Fi от настройките."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Регистриране с оператора ви"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s – обаждания през Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-bn-rBD/strings.xml b/core/res/res/values-mcc310-mnc160-bn-rBD/strings.xml
new file mode 100644
index 0000000..0c3e816
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-bn-rBD/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi এর মাধ্যমে কল করতে ও বার্তা পাঠাতে, প্রথমে আপনার পরিষেবা প্রদানকারীকে এই পরিষেবার সেট আপ করার বিষয়ে জিজ্ঞাসা করুন। তারপরে আবার সেটিংস থেকে Wi-Fi কলিং চালু করুন।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"আপনার পরিষেবা প্রদানকারীকে নথিভুক্ত করুন"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi কলিং"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ca/strings.xml b/core/res/res/values-mcc310-mnc160-ca/strings.xml
new file mode 100644
index 0000000..89baf1b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ca/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Per fer trucades i enviar missatges per Wi-Fi, primer has de demanar a l\'operador de telefonia mòbil que configuri aquest servei. Després, torna a activar les trucades per Wi-Fi des de Configuració."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registra\'t amb el teu operador de telefonia mòbil"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Trucada per Wi-Fi amb %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-cs/strings.xml b/core/res/res/values-mcc310-mnc160-cs/strings.xml
new file mode 100644
index 0000000..3eb962e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-cs/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Chcete-li volat a odesílat textové zprávy přes síť Wi-Fi, nejprve požádejte operátora, aby vám tuto službu nastavil. Poté volání přes Wi-Fi opět zapněte v Nastavení."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrace u operátora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Volání přes Wi-Fi: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-da/strings.xml b/core/res/res/values-mcc310-mnc160-da/strings.xml
new file mode 100644
index 0000000..8e401d8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-da/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Hvis du vil foretage opkald og sende beskeder via Wi-Fi, skal du først anmode dit mobilselskab om at konfigurere denne tjeneste. Derefter skal du slå Wi-Fi-opkald til igen fra Indstillinger."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrer dig hos dit mobilselskab"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi-opkald"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-de/strings.xml b/core/res/res/values-mcc310-mnc160-de/strings.xml
new file mode 100644
index 0000000..7b172d8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-de/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Um über WLAN telefonieren und Nachrichten senden zu können, bitte zuerst deinen Mobilfunkanbieter, diesen Dienst einzurichten. Aktiviere die Option \"Anrufe über WLAN\" dann erneut über die Einstellungen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registriere dich bei deinem Mobilfunkanbieter."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Anrufe über WLAN"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-el/strings.xml b/core/res/res/values-mcc310-mnc160-el/strings.xml
new file mode 100644
index 0000000..bfd09c0
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-el/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Για να κάνετε κλήσεις και να στέλνετε μηνύματα μέσω Wi-Fi, ζητήστε πρώτα από την εταιρεία κινητής τηλεφωνίας να ρυθμίσει την υπηρεσία. Στη συνέχεια, ενεργοποιήστε ξανά τη λειτουργία κλήσεων μέσω Wi-Fi από τις Ρυθμίσεις."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Εγγραφείτε μέσω της εταιρείας κινητής τηλεφωνίας"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Κλήση Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-en-rAU/strings.xml b/core/res/res/values-mcc310-mnc160-en-rAU/strings.xml
new file mode 100644
index 0000000..d4f59ed
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-en-rAU/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-en-rGB/strings.xml b/core/res/res/values-mcc310-mnc160-en-rGB/strings.xml
new file mode 100644
index 0000000..d4f59ed
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-en-rGB/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-en-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-en-rIN/strings.xml
new file mode 100644
index 0000000..d4f59ed
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-en-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-es-rUS/strings.xml b/core/res/res/values-mcc310-mnc160-es-rUS/strings.xml
new file mode 100644
index 0000000..8930a3e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-es-rUS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Para realizar llamadas o enviar mensajes por Wi-Fi, primero solicítale al proveedor que instale el servicio. Luego, vuelve a activar las llamadas por Wi-Fi desde Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Regístrate con tu proveedor."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Llamada por Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-es/strings.xml b/core/res/res/values-mcc310-mnc160-es/strings.xml
new file mode 100644
index 0000000..1aac00b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-es/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Para hacer llamadas y enviar mensajes por Wi-Fi, debes pedir antes a tu operador que configure este servicio. Una vez hecho esto, vuelva a activar las llamadas Wi-Fi en Ajustes."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Regístrate con tu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Llamada Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-et-rEE/strings.xml b/core/res/res/values-mcc310-mnc160-et-rEE/strings.xml
new file mode 100644
index 0000000..c3be115
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-et-rEE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"WiFi-võrgu kaudu helistamiseks ja sõnumite saatmiseks paluge operaatoril esmalt see teenus seadistada. Seejärel lülitage WiFi-kõned menüüs Seaded uuesti sisse."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registreeruge operaatori juures"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Operaatori %s WiFi-kõned"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-eu-rES/strings.xml b/core/res/res/values-mcc310-mnc160-eu-rES/strings.xml
new file mode 100644
index 0000000..93c4026
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-eu-rES/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wi-Fi bidezko deiak Ezarpenak atalean."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Erregistratu operadorearekin"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi bidezko deiak"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-fa/strings.xml b/core/res/res/values-mcc310-mnc160-fa/strings.xml
new file mode 100644
index 0000000..247693a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-fa/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"برای برقراری تماس و ارسال پیام از طریق Wi-Fi، ابتدا از شرکت مخابراتیتان درخواست کنید این سرویس را راهاندازی کند. سپس دوباره از تنظیمات، تماس Wi-Fi را روشن کنید."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ثبت نام با شرکت مخابراتی شما"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"تماس %s Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-fi/strings.xml b/core/res/res/values-mcc310-mnc160-fi/strings.xml
new file mode 100644
index 0000000..aebdf71
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-fi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Jos haluat soittaa puheluita ja lähettää viestejä Wi-Fin kautta, pyydä ensin operaattoriasi ottamaan tämä palvelu käyttöön. Ota sitten Wi-Fi-puhelut käyttöön asetuksissa."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Rekisteröidy operaattorisi asiakkaaksi."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Wi-Fi-puhelut: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-fr-rCA/strings.xml b/core/res/res/values-mcc310-mnc160-fr-rCA/strings.xml
new file mode 100644
index 0000000..b0d21c2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-fr-rCA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Pour effectuer des appels et envoyer des messages par Wi-Fi, demandez tout d\'abord à votre fournisseur de services de configurer ce service. Réactivez ensuite les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Inscrivez-vous auprès de votre fournisseur de services"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-fr/strings.xml b/core/res/res/values-mcc310-mnc160-fr/strings.xml
new file mode 100644
index 0000000..9fe787c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-fr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Pour effectuer des appels et envoyer des messages via le Wi-Fi, demandez tout d\'abord à votre opérateur de configurer ce service. Réactivez ensuite les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Inscrivez-vous auprès de votre opérateur."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-gl-rES/strings.xml b/core/res/res/values-mcc310-mnc160-gl-rES/strings.xml
new file mode 100644
index 0000000..f02d667
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-gl-rES/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Para facer chamadas e enviar mensaxes a través da wifi, primeiro pídelle ao teu operador que configure este servizo. A continuación, activa de novo as chamadas por wifi en Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Rexístrate co teu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Chamadas por wifi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-gu-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-gu-rIN/strings.xml
new file mode 100644
index 0000000..e218ee6
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-gu-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi પર કૉલ્સ કરવા અને સંદેશા મોકલવા માટે, પહેલા તમારા કેરીઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગ્સમાંથી Wi-Fi કૉલિંગ ચાલુ કરો."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"તમારા કેરીઅર સાથે નોંધણી કરો"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi કૉલિંગ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-hi/strings.xml b/core/res/res/values-mcc310-mnc160-hi/strings.xml
new file mode 100644
index 0000000..23f4dc8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-hi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"वाई-फ़ाई से कॉल करने और संदेश भेजने के लिए, सबसे पहले अपने वाहक से इस सेवा को सेट करने के लिए कहें. उसके बाद सेटिंग से पुन: वाई-फ़ाई कॉलिंग चालू करें."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"अपने वाहक के साथ पंजीकृत करें"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s वाई-फ़ाई कॉलिंग"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-hr/strings.xml b/core/res/res/values-mcc310-mnc160-hr/strings.xml
new file mode 100644
index 0000000..956093a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-hr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Da biste telefonirali i slali pozive putem Wi-Fi-ja, morate tražiti od mobilnog operatera da vam postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozive u Postavkama."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrirajte se kod mobilnog operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi pozivanje"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-hu/strings.xml b/core/res/res/values-mcc310-mnc160-hu/strings.xml
new file mode 100644
index 0000000..7dbf9e8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-hu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Ha Wi-Fin szeretne telefonálni és üzenetet küldeni, kérje meg szolgáltatóját, hogy állítsa be ezt a szolgáltatást. Ezután a Beállítások menüben kapcsolhatja be újra a Wi-Fi-hívást."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Regisztráljon szolgáltatójánál"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi-hívás"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-hy-rAM/strings.xml b/core/res/res/values-mcc310-mnc160-hy-rAM/strings.xml
new file mode 100644
index 0000000..aadc509
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-hy-rAM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi-ի միջոցով զանգեր կատարելու և հաղորդագրություններ ուղարկելու համար նախ դիմեք ձեր օպերատորին՝ ծառայությունը կարգավորելու համար: Ապա նորից միացրեք Wi-Fi զանգերը Կարգավորումներում:"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Գրանցվեք օպերատորի մոտ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi զանգեր"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-in/strings.xml b/core/res/res/values-mcc310-mnc160-in/strings.xml
new file mode 100644
index 0000000..3640291
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-in/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Untuk melakukan panggilan telepon dan mengirim pesan melalui Wi-Fi, terlebih dahulu minta operator untuk menyiapkan layanan ini. Lalu, aktifkan lagi panggilan telepon Wi-Fi dari Setelan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Harap daftarkan ke operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Panggilan Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-is-rIS/strings.xml b/core/res/res/values-mcc310-mnc160-is-rIS/strings.xml
new file mode 100644
index 0000000..fd10da5
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-is-rIS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Til að hringja og senda skilaboð yfir Wi-Fi þarftu fyrst að biðja símafyrirtækið þitt um að setja þá þjónustu upp. Kveiktu síðan á Wi-Fi símtölum í stillingunum."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Skráðu þig hjá símafyrirtækinu"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi símtöl"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-it/strings.xml b/core/res/res/values-mcc310-mnc160-it/strings.xml
new file mode 100644
index 0000000..d8479d3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-it/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Per poter effettuare chiamate e inviare messaggi tramite Wi-Fi, devi chiedere all\'operatore di attivare il servizio. Dopodiché, riattiva le chiamate Wi-Fi dalle Impostazioni."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrati con il tuo operatore"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Chiamate Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-iw/strings.xml b/core/res/res/values-mcc310-mnc160-iw/strings.xml
new file mode 100644
index 0000000..2aa3937
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-iw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"כדי להתקשר ולשלוח הודעות ברשת Wi-Fi, תחילה יש לבקש מהספק להגדיר את השירות. לאחר מכן, יש להפעיל שוב התקשרות Wi-Fi מ\'הגדרות\'."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"הירשם אצל הספק"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"שיחות Wi-Fi של %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ja/strings.xml b/core/res/res/values-mcc310-mnc160-ja/strings.xml
new file mode 100644
index 0000000..e589334
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ja/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi経由で音声通話の発信やメッセージの送信を行うには、携帯通信会社にWi-Fiサービスを申し込んだ上で、設定画面でWi-Fi発信を再度ONにしてください。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"携帯通信会社に登録してください"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Wi-Fi通話(%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ka-rGE/strings.xml b/core/res/res/values-mcc310-mnc160-ka-rGE/strings.xml
new file mode 100644
index 0000000..2b8fd07
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ka-rGE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi-ს მეშვეობით ზარების განხორციელების ან შეტყობინების გაგზავნისათვის, პირველ რიგში დაეკითხეთ თქვენს ოპერატორს აღნიშნულ მომსახურებაზე. შემდეგ ხელახლა ჩართეთ Wi-Fi ზარები პარამეტრებიდან."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"დაარეგისტრირეთ თქვენი ოპერატორი"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s დარეკვა Wi-Fi-ს მეშვეობით"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-kk-rKZ/strings.xml b/core/res/res/values-mcc310-mnc160-kk-rKZ/strings.xml
new file mode 100644
index 0000000..b4f2433
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-kk-rKZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi арқылы қоңырау шалу және хабарларды жіберу үшін алдымен жабдықтаушыңыздан осы қызметті орнатуды сұраңыз. Содан кейін Параметрлерден Wi-Fi қоңырау шалуын іске қосыңыз."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Жабдықтаушыңыз арқылы тіркелу"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi арқылы қоңырау шалу"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-km-rKH/strings.xml b/core/res/res/values-mcc310-mnc160-km-rKH/strings.xml
new file mode 100644
index 0000000..1806fbc
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-km-rKH/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"ដើម្បីធ្វើការហៅ និងផ្ញើសារតាម Wi-Fi ដំបូងឡើយអ្នកត្រូវស្នើឲ្យក្រុមហ៊ុនរបស់អ្នកដំឡើងសេវាកម្មនេះសិន។ បន្ទាប់មកបើកការហៅតាម Wi-Fi ម្តងទៀតចេញពីការកំណត់។"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ចុះឈ្មោះជាមួយក្រុមហ៊ុនរបស់អ្នក"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"ការហៅតាមរយៈ Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-kn-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-kn-rIN/strings.xml
new file mode 100644
index 0000000..43d942a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-kn-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi ಬಳಸಿಕೊಂಡು ಕರೆ ಮಾಡಲು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, ಮೊದಲು ಈ ಸಾಧನವನ್ನು ಹೊಂದಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಕೇಳಿ. ತದನಂತರ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಮತ್ತೆ Wi-Fi ಆನ್ ಮಾಡಿ."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ನಿಮ್ಮ ವಾಹಕದಲ್ಲಿ ನೋಂದಾಯಿಸಿಕೊಳ್ಳಿ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi ಕರೆ ಮಾಡುವಿಕೆ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ko/strings.xml b/core/res/res/values-mcc310-mnc160-ko/strings.xml
new file mode 100644
index 0000000..9e13223
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ko/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi를 사용하여 전화를 걸고 메시지를 보내려면 먼저 이동통신사에 문의하여 이 기능을 설정해야 합니다. 그런 다음 설정에서 Wi-Fi 통화를 사용 설정하시기 바랍니다."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"이동통신사에 등록"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi 통화"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ky-rKG/strings.xml b/core/res/res/values-mcc310-mnc160-ky-rKG/strings.xml
new file mode 100644
index 0000000..8b88ac1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ky-rKG/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi аркылуу чалууларды аткарып жана билдирүүлөрдү жөнөтүү үчүн адегенде байланыш операторуңуздан бул кызматты орнотушун сураныңыз. Андан соң, Жөндөөлөрдөн Wi-Fi чалууну кайра күйгүзүңүз."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Операторуңузга катталыңыз"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi чалуу"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-lo-rLA/strings.xml b/core/res/res/values-mcc310-mnc160-lo-rLA/strings.xml
new file mode 100644
index 0000000..a5347a9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-lo-rLA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"ເພື່ອໂທ ແລະສົ່ງຂໍ້ຄວາມຢູ່ເທິງ Wi-Fi, ກ່ອນອື່ນໝົດໃຫ້ຖ້າມຜູ້ໃຫ້ບໍລິການເຄືອຂ່າຍຂອງທ່ານ ເພື່ອຕັ້ງການບໍລິການນີ້. ຈາກນັ້ນເປີດການໂທ Wi-Fi ອີກຈາກການຕັ້ງຄ່າ."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ລົງທະບຽນກັບຜູ້ໃຫ້ບໍລິການເຄືອຂ່າຍຂອງທ່ານ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"ການໂທ %s Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-lt/strings.xml b/core/res/res/values-mcc310-mnc160-lt/strings.xml
new file mode 100644
index 0000000..81b7488
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-lt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Jei norite skambinti ir siųsti pranešimus „Wi-Fi“ ryšiu, pirmiausia paprašykite operatoriaus nustatyti šią paslaugą. Tada vėl įjunkite skambinimą „Wi-Fi“ ryšiu „Nustatymų“ skiltyje."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Užregistruokite pas operatorių"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"„%s“ „Wi-Fi“ skambinimas"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-lv/strings.xml b/core/res/res/values-mcc310-mnc160-lv/strings.xml
new file mode 100644
index 0000000..5d24c5f
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-lv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Lai veiktu zvanus un sūtītu īsziņas Wi-Fi tīklā, vispirms lūdziet mobilo sakaru operatoru iestatīt šo pakalpojumu. Pēc tam iestatījumos vēlreiz ieslēdziet Wi-Fi zvanus."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Reģistrēt to pie sava mobilo sakaru operatora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi zvani"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-mk-rMK/strings.xml b/core/res/res/values-mcc310-mnc160-mk-rMK/strings.xml
new file mode 100644
index 0000000..aea280e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-mk-rMK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"За повикување и испраќање пораки преку Wi-Fi, прво побарајте од операторот да ви ја постави оваа услуга. Потоа повторно вклучете повикување преку Wi-Fi во Поставки."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Регистрирајте се со операторот"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Повикување преку Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ml-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-ml-rIN/strings.xml
new file mode 100644
index 0000000..9e244eb
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ml-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"വൈഫൈ വഴി കോളുകൾ വിളിക്കാനും സന്ദേശങ്ങൾ അയയ്ക്കാനും ആദ്യം നിങ്ങളുടെ കാരിയറോട് ഈ സേവനം സജ്ജമാക്കാൻ ആവശ്യപ്പെടുക. ക്രമീകരണത്തിൽ നിന്ന് വീണ്ടും വൈഫൈ കോളിംഗ് ഓണാക്കുക."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"നിങ്ങളുടെ കാരിയറിൽ രജിസ്റ്റർ ചെയ്യുക"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s വൈഫൈ കോളിംഗ്"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-mn-rMN/strings.xml b/core/res/res/values-mcc310-mnc160-mn-rMN/strings.xml
new file mode 100644
index 0000000..5b59d99
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-mn-rMN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi-аар дуудлага хийх болон мессеж илгээхээр бол эхлээд оператороосоо энэ төхөөрөмжийг тохируулж өгөхийг хүсээрэй. Дараа нь Тохиргооноос Wi-Fi дуудлага хийх үйлдлийг асаагаарай."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Операторт бүртгүүлэх"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi Дуудлага"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-mr-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-mr-rIN/strings.xml
new file mode 100644
index 0000000..168c36b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-mr-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"वाय-फायवरून कॉल करण्यासाठी आणि संदेश पाठविण्यासाठी, प्रथम आपल्या वाहकास ही सेवा सेट करण्यास सांगा. नंतर सेटिंग्जमधून पुन्हा वाय-फाय कॉलिंग चालू करा."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"आपल्या वाहकासह नोंदणी करा"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s वाय-फाय कॉलिंग"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ms-rMY/strings.xml b/core/res/res/values-mcc310-mnc160-ms-rMY/strings.xml
new file mode 100644
index 0000000..74bd9d6
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ms-rMY/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Untuk membuat panggilan dan menghantar mesej melalui Wi-Fi, mula-mula minta pembawa anda menyediakan perkhidmatan ini. Kemudian hidupkan panggilan Wi-Fi sekali lagi daripada Tetapan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Daftar dengan pembawa anda"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Panggilan Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-my-rMM/strings.xml b/core/res/res/values-mcc310-mnc160-my-rMM/strings.xml
new file mode 100644
index 0000000..ae87ae7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-my-rMM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"ဝိုင်ဖိုင်သုံး၍ ဖုန်းခေါ်ဆိုရန်နှင့် မက်စေ့ဂျ်များပို့ရန်၊ ဤဝန်ဆောင်မှုအား စတင်သုံးနိုင်ရန်အတွက် သင့် မိုဘိုင်းဝန်ဆောင်မှုအား ဦးစွာမေးမြန်းပါ။ ထို့နောက် ဆက်တင်မှတဆင့် ဝိုင်ဖိုင် ခေါ်ဆိုမှုအား ထပ်ဖွင့်ပါ။"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"သင့် မိုဘိုင်းဝန်ဆောင်မှုဖြင့် မှတ်ပုံတင်ရန်"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s ဝိုင်ဖိုင် ခေါ်ဆိုမှု"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-nb/strings.xml b/core/res/res/values-mcc310-mnc160-nb/strings.xml
new file mode 100644
index 0000000..34357e1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-nb/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"For å ringe og sende meldinger over Wi-Fi må du først be operatøren om å konfigurere denne tjenesten. Deretter slår du på Wi-Fi-anrop igjen fra Innstillinger."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrer deg hos operatøren din"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi-anrop"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ne-rNP/strings.xml b/core/res/res/values-mcc310-mnc160-ne-rNP/strings.xml
new file mode 100644
index 0000000..8956249
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ne-rNP/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi बाट कल गर्न र सन्देशहरू पठाउन, सबभन्दा पहिला यो सेवा सेटअप गर्न तपाईँको वाहकलाई भन्नुहोस्। त्यसपछि फेरि सेटिङहरूबाट Wi-Fi कलिङ सक्रिय पार्नुहोस्।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"आफ्नो वाहकसँग दर्ता गर्नुहोस्"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi कलिङ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-nl/strings.xml b/core/res/res/values-mcc310-mnc160-nl/strings.xml
new file mode 100644
index 0000000..319f799
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-nl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Als je wilt bellen en berichten wilt verzenden via wifi, moet je eerst je provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via \'Instellingen\'."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registreren bij je provider"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Bellen via wifi van %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-pa-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-pa-rIN/strings.xml
new file mode 100644
index 0000000..5641abe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-pa-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi ਤੇ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਲਈ, ਪਹਿਲਾਂ ਆਪਣੇ ਕੈਰੀਅਰ ਨੂੰ ਇਹ ਸੇਵਾ ਸੈਟ ਅਪ ਕਰਨ ਲਈ ਕਹੋ। ਫਿਰ ਸੈਟਿੰਗਾਂ ਵਿੱਚੋਂ Wi-Fi ਕਾਲਿੰਗ ਦੁਬਾਰਾ ਚਾਲੂ ਕਰੋ।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਰਜਿਸਟਰ ਕਰੋ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi ਕਾਲਿੰਗ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-pl/strings.xml b/core/res/res/values-mcc310-mnc160-pl/strings.xml
new file mode 100644
index 0000000..1d916cb
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-pl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Aby dzwonić i wysyłać wiadomości przez Wi-Fi, poproś swojego operatora o skonfigurowanie tej usługi. Potem ponownie włącz połączenia przez Wi-Fi w Ustawieniach."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Zarejestruj u operatora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Połączenia przez Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc160-pt-rBR/strings.xml
new file mode 100644
index 0000000..1c68cb1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-pt-rBR/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-pt-rPT/strings.xml b/core/res/res/values-mcc310-mnc160-pt-rPT/strings.xml
new file mode 100644
index 0000000..86dba8c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-pt-rPT/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Para fazer chamadas e enviar mensagens por Wi-Fi, comece por pedir ao seu operador para configurar este serviço. Em seguida, nas Definições, ative novamente as Chamadas Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registar-se junto do seu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Chamadas Wi-Fi da %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-pt/strings.xml b/core/res/res/values-mcc310-mnc160-pt/strings.xml
new file mode 100644
index 0000000..1c68cb1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-pt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ro/strings.xml b/core/res/res/values-mcc310-mnc160-ro/strings.xml
new file mode 100644
index 0000000..f2490d8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ro/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Pentru a apela și a trimite mesaje prin Wi-Fi, mai întâi solicitați configurarea acestui serviciu la operator. Apoi, activați din nou apelarea prin Wi-Fi din Setări."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Înregistrați-vă la operatorul dvs."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Apelare prin Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ru/strings.xml b/core/res/res/values-mcc310-mnc160-ru/strings.xml
new file mode 100644
index 0000000..9f2bc43
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ru/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Чтобы совершать звонки и отправлять сообщения по Wi-Fi, необходимо сначала обратиться к оператору связи и подключить эту услугу. После этого вы сможете снова выбрать этот параметр в настройках."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Укажите оператора и зарегистрируйтесь"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Звонки по Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-si-rLK/strings.xml b/core/res/res/values-mcc310-mnc160-si-rLK/strings.xml
new file mode 100644
index 0000000..0c13341
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-si-rLK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi හරහා ඇමතුම් සිදු කිරීමට සහ පණිවිඩ යැවීමට, පළමුව මෙම සේවාව පිහිටුවන ලෙස ඔබේ වාහකයෙන් ඉල්ලන්න. අනතුරුව සැකසීම් වෙතින් Wi-Fi ඇමතුම නැවත ක්රියාත්මක කරන්න."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ඔබගේ වාහකය සමඟ ලියාපදිංචි වන්න"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi අමතමින්"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-sk/strings.xml b/core/res/res/values-mcc310-mnc160-sk/strings.xml
new file mode 100644
index 0000000..3fe32f7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-sk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Ak chcete volať a odosielať správy prostredníctvom siete Wi-Fi, kontaktujte najskôr svojho operátora v súvislosti s nastavením tejto služby. Potom opäť zapnite v Nastaveniach volanie cez Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrujte sa so svojím operátorom"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Volanie siete Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-sl/strings.xml b/core/res/res/values-mcc310-mnc160-sl/strings.xml
new file mode 100644
index 0000000..3818309
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-sl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Če želite klicati ali pošiljati sporočila prek omrežja Wi-Fi, se najprej obrnite na operaterja, da nastavi to storitev. Nato v nastavitvah znova vklopite klicanje prek omrežja Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registracija pri operaterju"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Klicanje prek Wi-Fi-ja (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-sq-rAL/strings.xml b/core/res/res/values-mcc310-mnc160-sq-rAL/strings.xml
new file mode 100644
index 0000000..647cd03
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-sq-rAL/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Për të bërë telefonata dhe për të dërguar mesazhe me Wi-Fi, në fillim kërkoji operatorit celular ta konfigurojë këtë shërbim. Më pas aktivizo përsëri telefonatat me Wi-Fi, nga Cilësimet."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Regjistrohu me operatorin tënd celular"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Telefonatat me Wi-Fi nga %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-sr/strings.xml b/core/res/res/values-mcc310-mnc160-sr/strings.xml
new file mode 100644
index 0000000..7f8381a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-sr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Да бисте упућивали позиве и слали поруке преко Wi-Fi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко Wi-Fi-ја."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Региструјте се код мобилног оператера"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Wi-Fi позивање преко оператера %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-sv/strings.xml b/core/res/res/values-mcc310-mnc160-sv/strings.xml
new file mode 100644
index 0000000..e72b3b7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-sv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Om du vill ringa samtal och skicka meddelanden via Wi-Fi ber du först operatören att konfigurera tjänsten. Därefter kan du aktivera Wi-Fi-samtal på nytt från Inställningar."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Registrera dig hos operatören"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi-samtal"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-sw/strings.xml b/core/res/res/values-mcc310-mnc160-sw/strings.xml
new file mode 100644
index 0000000..08ee7536
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-sw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Ili upige simu na kutuma ujumbe kupitia Wi-Fi, mwambie mtoa huduma wako asanidi huduma hii kwanza. Kisha uwashe tena upigaji simu kwa Wi-Fi kutoka kwenye Mipangilio."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Jisajili na mtoa huduma wako"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Upigaji Simu kwa Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ta-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-ta-rIN/strings.xml
new file mode 100644
index 0000000..d6ea49c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ta-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"வைஃபை மூலம் அழைக்க மற்றும் செய்திகள் அனுப்ப, முதலில் மொபைல் நிறுவனத்திடம் இந்தச் சேவையை அமைக்குமாறு கேட்கவும். பிறகு அமைப்புகளில் மீண்டும் வைஃபை அழைப்பை இயக்கவும்."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"உங்கள் மொபைல் நிறுவனத்தில் பதிவுசெய்யவும்"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s வைஃபை அழைப்பு"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-te-rIN/strings.xml b/core/res/res/values-mcc310-mnc160-te-rIN/strings.xml
new file mode 100644
index 0000000..61f3929
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-te-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fiలో కాల్లు చేయడం మరియు సందేశాలు పంపడం కోసం ముందుగా ఈ సేవను సెటప్ చేయడానికి మీ క్యారియర్ను అడగండి. ఆపై సెట్టింగ్ల నుండి మళ్లీ Wi-Fi కాలింగ్ను ఆన్ చేయండి."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"మీ క్యారియర్తో నమోదు చేయండి"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi కాలింగ్"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-th/strings.xml b/core/res/res/values-mcc310-mnc160-th/strings.xml
new file mode 100644
index 0000000..f1ff305
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-th/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"หากต้องการโทรออกและส่งข้อความผ่าน Wi-Fi โปรดสอบถามผู้ให้บริการของคุณก่อนเพื่อตั้งค่าบริการนี้ แล้วเปิดการโทรผ่าน Wi-Fi อีกครั้งจากการตั้งค่า"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"ลงทะเบียนกับผู้ให้บริการ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"กำลังเรียก Wi-Fi ของ %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-tl/strings.xml b/core/res/res/values-mcc310-mnc160-tl/strings.xml
new file mode 100644
index 0000000..380b410
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-tl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Upang tumawag at magpadala ng mga mensahe sa pamamagitan ng Wi-Fi, hilingin muna sa iyong carrier na i-set up ang serbisyong ito. Pagkatapos ay muling i-on ang pagtawag gamit ang Wi-Fi mula sa Mga Setting."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Magparehistro sa iyong carrier"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Pagtawag Gamit ang Wi-Fi ng %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-tr/strings.xml b/core/res/res/values-mcc310-mnc160-tr/strings.xml
new file mode 100644
index 0000000..2cb1dc9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-tr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra tekrar Ayarlar\'dan Kablosuz çağrı özelliğini açın."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Operatörünüze kaydolun"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Kablosuz Çağrı"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-uk/strings.xml b/core/res/res/values-mcc310-mnc160-uk/strings.xml
new file mode 100644
index 0000000..9e4d94c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-uk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Щоб телефонувати або надсилати повідомлення через Wi-Fi, спершу попросіть свого оператора налаштувати цю послугу. Після цього ввімкніть дзвінки через Wi-Fi у налаштуваннях."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Зареєструйтеся в оператора"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Дзвінок через Wi-Fi від оператора %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-ur-rPK/strings.xml b/core/res/res/values-mcc310-mnc160-ur-rPK/strings.xml
new file mode 100644
index 0000000..e617582
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-ur-rPK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi سے کالز کرنے اور پیغامات بھیجنے کیلئے، پہلے اپنے کیریئر سے اس سروس کو ترتیب دینے کیلئے کہیں۔ پھر ترتیبات سے دوبارہ Wi-Fi کالنگ آن کریں۔"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"اپنے کیریئر کے ساتھ رجسٹر کریں"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi کالنگ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-uz-rUZ/strings.xml b/core/res/res/values-mcc310-mnc160-uz-rUZ/strings.xml
new file mode 100644
index 0000000..a951fc4
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-uz-rUZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Wi-Fi orqali qo‘ng‘iroqlarni amalga oshirish va xabarlar bilan almashinish uchun uyali aloqa operatoringizdan ushbu xizmatni yoqib qo‘yishni so‘rashingiz lozim. Keyin sozlamalarda Wi-Fi qo‘ng‘irog‘i imkoniyatini yoqib olishingiz mumkin."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Mobil operatoringiz yordamida ro‘yxatdan o‘ting"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi qo‘ng‘iroqlar"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-vi/strings.xml b/core/res/res/values-mcc310-mnc160-vi/strings.xml
new file mode 100644
index 0000000..8f68be6
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-vi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Để gọi điện và gửi tin nhắn qua Wi-Fi, trước tiên hãy yêu cầu nhà cung cấp dịch vụ của bạn thiết lập dịch vụ này. Sau đó, bật lại Gọi qua Wi-Fi từ Cài đặt."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Đăng ký với nhà cung cấp dịch vụ của bạn"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"Gọi qua Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc160-zh-rCN/strings.xml
new file mode 100644
index 0000000..3b10a63
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-zh-rCN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"要通过 WLAN 打电话和发信息,请先让您的运营商开通此服务,然后再到“设置”中重新开启 WLAN 通话功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"向您的运营商注册"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s WLAN 通话功能"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc160-zh-rHK/strings.xml
new file mode 100644
index 0000000..a39b37c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-zh-rHK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"如要透過 Wi-Fi 撥打電話及傳送訊息,請先向您的流動網絡供應商要求設定此服務。然後再次在「設定」中開啟 Wi-Fi 通話。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"向您的流動網絡供應商註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-zh-rTW/strings.xml b/core/res/res/values-mcc310-mnc160-zh-rTW/strings.xml
new file mode 100644
index 0000000..28690aa
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-zh-rTW/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"如要透過 Wi-FI 撥打電話及傳送訊息,請先要求您的行動通訊業者開通這項服務,然後再到「設定」啟用 Wi-Fi 通話功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"向您的行動通訊業者註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc160-zu/strings.xml b/core/res/res/values-mcc310-mnc160-zu/strings.xml
new file mode 100644
index 0000000..d08486d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160-zu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="3017901214286816293">"Ukuze wenze amakholi uphinde uthumele imilayezo nge-Wi-Fi, qala ucele inkampani yakho yenethiwekhi ukuthi isethe le divayisi. Bese uvula ukushaya kwe-Wi-Fi futhi kusukela kuzilungiselelo."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="7068215934335709161">"Bhalisa ngenkampani yakho yenethiwekhi"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="2031160810542298336">"%s ukushaya kwe-Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-af/strings.xml b/core/res/res/values-mcc310-mnc200-af/strings.xml
new file mode 100644
index 0000000..22d2685
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-af/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Om oproepe te maak en boodskappe oor Wi-Fi te stuur, vra jou diensverskaffer eers om hierdie diens op te stel. Skakel Wi-Fi-oproepe dan weer in Instellings aan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registreer by jou diensverskaffer"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s-Wi-Fi-oproepe"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-am/strings.xml b/core/res/res/values-mcc310-mnc200-am/strings.xml
new file mode 100644
index 0000000..6592d18
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-am/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"በWi-Fi ላይ ጥሪዎችን ለማድረግ እና መልዕክቶችን ለመላክ መጀመሪያ የአገልግሎት አቅራቢዎ ይህን አገልግሎት እንዲያዘጋጅልዎ ይጠይቁ። ከዚያ ከቅንብሮች ሆነው እንደገና የWi-Fi ጥሪን ያብሩ።"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"የአገልግሎት አቅራቢዎ ጋር ይመዝገቡ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"የ%s Wi-Fi ጥሪ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ar/strings.xml b/core/res/res/values-mcc310-mnc200-ar/strings.xml
new file mode 100644
index 0000000..ca411f6
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ar/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"لإجراء مكالمات وإرسال رسائل عبر Wi-Fi، اطلب من مشغّل شبكة الجوّال أولاً إعداد هذه الخدمة، ثم شغّل الاتصال عبر Wi-Fi مرة أخرى من خلال الإعدادات."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"التسجيل لدى مشغّل شبكة الجوّال"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s جارٍ الاتصال عبر Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-az-rAZ/strings.xml b/core/res/res/values-mcc310-mnc200-az-rAZ/strings.xml
new file mode 100644
index 0000000..94e7f88
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-az-rAZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi üzərindən zəng etmək və mesaj göndərmək üçün ilk öncə operatordan bu xidməti ayarlamağı tələb edin. Sonra Ayarlardan Wi-Fi çağrısını aktivləşdirin."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Operatorla qeydiyyatdan keçin"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi Zəngi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc200-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..3dc4fff
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-b+sr+Latn/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Da biste upućivali pozive i slali poruke preko Wi-Fi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko Wi-Fi-ja."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrujte se kod mobilnog operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Wi-Fi pozivanje preko operatera %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-bg/strings.xml b/core/res/res/values-mcc310-mnc200-bg/strings.xml
new file mode 100644
index 0000000..b190b7d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-bg/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"За да извършвате обаждания и да изпращате съобщения през Wi-Fi, първо, помолете оператора си да настрои тази услуга. След това включете отново функцията за обаждания през Wi-Fi от настройките."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Регистриране с оператора ви"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s – обаждания през Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-bn-rBD/strings.xml b/core/res/res/values-mcc310-mnc200-bn-rBD/strings.xml
new file mode 100644
index 0000000..26d6156
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-bn-rBD/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi এর মাধ্যমে কল করতে ও বার্তা পাঠাতে, প্রথমে আপনার পরিষেবা প্রদানকারীকে এই পরিষেবার সেট আপ করার বিষয়ে জিজ্ঞাসা করুন। তারপরে আবার সেটিংস থেকে Wi-Fi কলিং চালু করুন।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"আপনার পরিষেবা প্রদানকারীর সাথে নথিভুক্ত করুন"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi কলিং"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-bs-rBA/strings.xml b/core/res/res/values-mcc310-mnc200-bs-rBA/strings.xml
new file mode 100644
index 0000000..d9913af
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-bs-rBA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Da biste pozivali i slali poruke preko Wi-Fi-ja, prvo zatražite od operatera da postavi tu uslugu. Potom u Postavkama ponovo uključite Wi-Fi pozivanje."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrirajte se kod operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi pozivanje"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ca/strings.xml b/core/res/res/values-mcc310-mnc200-ca/strings.xml
new file mode 100644
index 0000000..17e9a96
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ca/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Per fer trucades i enviar missatges per Wi-Fi, primer has de demanar a l\'operador de telefonia mòbil que configuri aquest servei. Després, torna a activar les trucades per Wi-Fi des de Configuració."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registra\'t amb el teu operador de telefonia mòbil"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Trucades per Wi-Fi amb %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-cs/strings.xml b/core/res/res/values-mcc310-mnc200-cs/strings.xml
new file mode 100644
index 0000000..edfd91e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-cs/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Chcete-li volat a odesílat textové zprávy přes síť Wi-Fi, nejprve požádejte operátora, aby vám tuto službu nastavil. Poté volání přes Wi-Fi opět zapněte v Nastavení."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrace u operátora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Volání přes Wi-Fi: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-da/strings.xml b/core/res/res/values-mcc310-mnc200-da/strings.xml
new file mode 100644
index 0000000..4fa58c9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-da/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Før du kan foretage opkald og sende beskeder via Wi-Fi, skal du anmode dit mobilselskab om at konfigurere denne tjeneste. Du skal derefter slå Wi-Fi-opkald til igen fra Indstillinger."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrer dig hos dit mobilselskab"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi-opkald"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-de/strings.xml b/core/res/res/values-mcc310-mnc200-de/strings.xml
new file mode 100644
index 0000000..83fca41
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-de/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Um über WLAN telefonieren und Nachrichten senden zu können, bitte zuerst deinen Mobilfunkanbieter, diesen Dienst einzurichten. Aktiviere die Option \"Anrufe über WLAN\" dann noch einmal über die Einstellungen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registriere dich bei deinem Mobilfunkanbieter"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s-WLAN-Anrufe"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-el/strings.xml b/core/res/res/values-mcc310-mnc200-el/strings.xml
new file mode 100644
index 0000000..3e01614
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-el/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Για να κάνετε κλήσεις και να στέλνετε μηνύματα μέσω Wi-Fi, ζητήστε πρώτα από την εταιρεία κινητής τηλεφωνίας που χρησιμοποιείτε να ρυθμίσει την υπηρεσία. Στη συνέχεια, ενεργοποιήστε ξανά τη λειτουργία Κλήσης Wi-Fi από τις Ρυθμίσεις."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Εγγραφείτε μέσω της εταιρείας κινητής τηλεφωνίας"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Κλήση Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-en-rAU/strings.xml b/core/res/res/values-mcc310-mnc200-en-rAU/strings.xml
new file mode 100644
index 0000000..78d5efe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-en-rAU/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-en-rGB/strings.xml b/core/res/res/values-mcc310-mnc200-en-rGB/strings.xml
new file mode 100644
index 0000000..78d5efe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-en-rGB/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-en-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-en-rIN/strings.xml
new file mode 100644
index 0000000..78d5efe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-en-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-es-rUS/strings.xml b/core/res/res/values-mcc310-mnc200-es-rUS/strings.xml
new file mode 100644
index 0000000..f7e80c3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-es-rUS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Para realizar llamadas y enviar mensajes con Wi-Fi, primero solicítale al proveedor que instale el servicio. Luego, vuelve a activar Llamada con Wi-Fi en Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Regístrate con tu proveedor"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Llamada con Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-es/strings.xml b/core/res/res/values-mcc310-mnc200-es/strings.xml
new file mode 100644
index 0000000..9ec401a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-es/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Para hacer llamadas y enviar mensajes por Wi-Fi, solicita a tu operador que configure este servicio y, cuando lo haga, vuelve a activar las llamadas por Wi-Fi en Ajustes."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Regístrate con tu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Llamada por Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-et-rEE/strings.xml b/core/res/res/values-mcc310-mnc200-et-rEE/strings.xml
new file mode 100644
index 0000000..0635966
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-et-rEE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"WiFi-võrgu kaudu helistamiseks ja sõnumite saatmiseks paluge operaatoril esmalt see teenus seadistada. Seejärel lülitage WiFi-kõned menüüs Seaded uuesti sisse."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registreeruge operaatori juures"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Operaatori %s WiFi-kõned"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-eu-rES/strings.xml b/core/res/res/values-mcc310-mnc200-eu-rES/strings.xml
new file mode 100644
index 0000000..5f8d8dc
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-eu-rES/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wi-Fi bidezko deiak Ezarpenak atalean."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Erregistratu operadorearekin"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi bidezko deiak"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-fa/strings.xml b/core/res/res/values-mcc310-mnc200-fa/strings.xml
new file mode 100644
index 0000000..698e254
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-fa/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"برای برقراری تماس و ارسال پیام از طریق Wi-Fi، ابتدا از شرکت مخابراتیتان درخواست کنید این سرویس را راهاندازی کند. سپس دوباره در «تنظیمات»، تماس از طریق Wi-Fi را روشن کنید."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"از طریق شرکت مخابراتیتان ثبتنام کنید"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"تماس از طریق %s Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-fi/strings.xml b/core/res/res/values-mcc310-mnc200-fi/strings.xml
new file mode 100644
index 0000000..d011bd5
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-fi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Jos haluat soittaa puheluita ja lähettää viestejä Wi-Fin kautta, pyydä ensin operaattoriasi ottamaan tämä palvelu käyttöön. Ota sitten Wi-Fi-puhelut käyttöön asetuksissa."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Rekisteröidy operaattorisi asiakkaaksi."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Wi-Fi-puhelut: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-fr-rCA/strings.xml b/core/res/res/values-mcc310-mnc200-fr-rCA/strings.xml
new file mode 100644
index 0000000..c69de43
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-fr-rCA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Pour effectuer des appels et envoyer des messages par Wi-Fi, demandez tout d\'abord à votre fournisseur de services de configurer ce service. Réactivez ensuite les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Inscrivez-vous auprès de votre fournisseur de services"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-fr/strings.xml b/core/res/res/values-mcc310-mnc200-fr/strings.xml
new file mode 100644
index 0000000..cbc6d38
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-fr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Pour passer des appels et envoyer des messages via le Wi-Fi, demandez d\'abord à votre opérateur de configurer ce service. Ensuite, réactivez les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Inscrivez-vous auprès de votre opérateur."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-gl-rES/strings.xml b/core/res/res/values-mcc310-mnc200-gl-rES/strings.xml
new file mode 100644
index 0000000..1736d77
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-gl-rES/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Para facer chamadas e enviar mensaxes a través da wifi, primeiro pídelle ao teu operador que configure este servizo. A continuación, activa de novo as chamadas por wifi en Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Rexístrate co teu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Chamadas por wifi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-gu-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-gu-rIN/strings.xml
new file mode 100644
index 0000000..a78ed0d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-gu-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi પર કૉલ્સ કરવા અને સંદેશા મોકલવા માટે, પહેલા તમારા કેરીઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગ્સમાંથી Wi-Fi કૉલિંગ ચાલુ કરો."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"તમારા કેરીઅર સાથે નોંધણી કરો"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi કૉલિંગ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-hi/strings.xml b/core/res/res/values-mcc310-mnc200-hi/strings.xml
new file mode 100644
index 0000000..1d8272a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-hi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"वाई-फ़ाई से कॉल करने और संदेश भेजने के लिए, सबसे पहले अपने वाहक से इस सेवा को सेट करने के लिए कहें. उसके बाद सेटिंग से पुन: वाई-फ़ाई कॉलिंग चालू करें."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"अपने वाहक के साथ पंजीकृत करें"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s वाई-फ़ाई कॉलिंग"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-hr/strings.xml b/core/res/res/values-mcc310-mnc200-hr/strings.xml
new file mode 100644
index 0000000..7977d96
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-hr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Da biste telefonirali i slali poruke putem Wi-Fi-ja, od mobilnog operatera morate tražiti da vam postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozive u postavkama."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrirajte se kod mobilnog operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi pozivi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-hu/strings.xml b/core/res/res/values-mcc310-mnc200-hu/strings.xml
new file mode 100644
index 0000000..daaf4f1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-hu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Ha Wi-Fin szeretne telefonálni és üzenetet küldeni, kérje meg szolgáltatóját, hogy állítsa be ezt a szolgáltatást. Ezután a Beállítások menüben kapcsolhatja be újra a Wi-Fi-hívást."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Regisztráljon szolgáltatójánál"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi-hívás"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-hy-rAM/strings.xml b/core/res/res/values-mcc310-mnc200-hy-rAM/strings.xml
new file mode 100644
index 0000000..36040e9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-hy-rAM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi-ի միջոցով զանգեր կատարելու և հաղորդագրություններ ուղարկելու համար նախ դիմեք ձեր օպերատորին՝ ծառայությունը կարգավորելու համար: Ապա նորից միացրեք Wi-Fi զանգերը Կարգավորումներում:"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Գրանցվեք օպերատորի մոտ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi զանգեր"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-in/strings.xml b/core/res/res/values-mcc310-mnc200-in/strings.xml
new file mode 100644
index 0000000..c71dbf7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-in/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Untuk melakukan panggilan telepon dan mengirim pesan melalui Wi-Fi, terlebih dahulu minta operator untuk menyiapkan layanan ini. Lalu, aktifkan lagi panggilan telepon Wi-Fi dari Setelan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Harap daftarkan ke operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Panggilan Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-is-rIS/strings.xml b/core/res/res/values-mcc310-mnc200-is-rIS/strings.xml
new file mode 100644
index 0000000..8c66836
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-is-rIS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Til að hringja og senda skilaboð yfir Wi-Fi þarftu fyrst að biðja símafyrirtækið þitt um að setja þá þjónustu upp. Kveiktu síðan á Wi-Fi símtölum í stillingunum."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Skráðu þig hjá símafyrirtækinu"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi símtöl"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-it/strings.xml b/core/res/res/values-mcc310-mnc200-it/strings.xml
new file mode 100644
index 0000000..cec5d8b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-it/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Per poter effettuare chiamate e inviare messaggi tramite Wi-Fi, devi chiedere all\'operatore di attivare il servizio. Dopodiché, riattiva le chiamate Wi-Fi dalle Impostazioni."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrati con il tuo operatore"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Chiamate Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-iw/strings.xml b/core/res/res/values-mcc310-mnc200-iw/strings.xml
new file mode 100644
index 0000000..c409606
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-iw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"כדי להתקשר ולשלוח הודעות ברשת Wi-Fi, תחילה יש לבקש מהספק להגדיר את השירות. לאחר מכן, יש להפעיל שוב שיחות Wi-Fi ב\'הגדרות\'."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"הירשם אצל הספק"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"שיחות Wi-Fi של %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ja/strings.xml b/core/res/res/values-mcc310-mnc200-ja/strings.xml
new file mode 100644
index 0000000..0486812
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ja/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi 経由で音声通話の発信やメッセージの送信を行うには、携帯通信会社に Wi-Fi サービスを申し込んだ上で、設定画面で Wi-Fi 発信を再度 ON にしてください。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"携帯通信会社に登録してください"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Wi-Fi 通話(%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ka-rGE/strings.xml b/core/res/res/values-mcc310-mnc200-ka-rGE/strings.xml
new file mode 100644
index 0000000..cb1734a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ka-rGE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi-ს მეშვეობით ზარების განსახორციელებლად ან შეტყობინებების გასაგზავნად, პირველ რიგში, ამ სერვისის გააქტიურება თქვენს ოპერატორს უნდა თხოვოთ. შემდეგ ხელახლა ჩართეთ Wi-Fi დარეკვა პარამეტრებიდან."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"დარეგისტრირდით თქვენი ოპერატორის მეშვეობით"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Wi-Fi დარეკვა (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-kk-rKZ/strings.xml b/core/res/res/values-mcc310-mnc200-kk-rKZ/strings.xml
new file mode 100644
index 0000000..a7ba1f1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-kk-rKZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi арқылы қоңырау шалу және хабарларды жіберу үшін алдымен жабдықтаушыңыздан осы қызметті орнатуды сұраңыз. Содан кейін \"Параметрлер\" тармағында Wi-Fi қоңырауларын қосыңыз."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Оператор арқылы тіркелу"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi қоңыраулары"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-km-rKH/strings.xml b/core/res/res/values-mcc310-mnc200-km-rKH/strings.xml
new file mode 100644
index 0000000..a0871a8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-km-rKH/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"ដើម្បីធ្វើការហៅ និងផ្ញើសារតាម Wi-Fi ដំបូងឡើយអ្នកត្រូវស្នើឲ្យក្រុមហ៊ុនរបស់អ្នកដំឡើងសេវាកម្មនេះសិន។ បន្ទាប់មកបើកការហៅតាម Wi-Fi ម្តងទៀតចេញពីការកំណត់។"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"ចុះឈ្មោះជាមួយក្រុមហ៊ុនរបស់អ្នក"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"ការហៅតាមរយៈ Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-kn-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-kn-rIN/strings.xml
new file mode 100644
index 0000000..4428f82
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-kn-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"ವೈ-ಫೈ ಬಳಸಿಕೊಂಡು ಕರೆ ಮಾಡಲು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, ಮೊದಲು ಈ ಸಾಧನವನ್ನು ಹೊಂದಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಕೇಳಿ. ತದನಂತರ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಮತ್ತೆ ವೈ-ಫೈ ಆನ್ ಮಾಡಿ."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"ನಿಮ್ಮ ವಾಹಕದಲ್ಲಿ ನೋಂದಾಯಿಸಿಕೊಳ್ಳಿ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ko/strings.xml b/core/res/res/values-mcc310-mnc200-ko/strings.xml
new file mode 100644
index 0000000..b42cc05
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ko/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi를 사용하여 전화를 걸고 메시지를 보내려면 먼저 이동통신사에 문의하여 이 기능을 설정해야 합니다. 그런 다음 설정에서 Wi-Fi 통화를 사용 설정하시기 바랍니다."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"이동통신사에 등록"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi 통화"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ky-rKG/strings.xml b/core/res/res/values-mcc310-mnc200-ky-rKG/strings.xml
new file mode 100644
index 0000000..896afde
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ky-rKG/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi аркылуу чалууларды аткарып жана билдирүүлөрдү жөнөтүү үчүн адегенде операторуңуздан бул кызматты орнотушун сураныңыз. Андан соң, Жөндөөлөрдөн Wi-Fi чалууну кайра күйгүзүңүз."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Операторуңузга катталыңыз"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi чалуу"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-lo-rLA/strings.xml b/core/res/res/values-mcc310-mnc200-lo-rLA/strings.xml
new file mode 100644
index 0000000..4476900
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-lo-rLA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"ເພື່ອໂທ ແລະ ສົ່ງຂໍ້ຄວາມຜ່ານ Wi-Fi, ໃຫ້ແຈ້ງຜູ້ໃຫ້ບໍລິການເຄືອຂ່າຍຂອງທ່ານເພື່ອຕັ້ງບໍລິການນີ້ກ່ອນ. ຈາກນັ້ນ ເປີດການໂທ Wi-Fi ອີກເທື່ອໜຶ່ງຈາກການຕັ້ງຄ່າ."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"ລົງທະບຽນນໍາຜູ້ໃຫ້ບໍລິການເຄືອຂ່າຍຂອງທ່ານ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"ການໂທ %s Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-lt/strings.xml b/core/res/res/values-mcc310-mnc200-lt/strings.xml
new file mode 100644
index 0000000..4fb0510
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-lt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Jei norite skambinti ir siųsti pranešimus naudodami „Wi-Fi“, pirmiausia paprašykite operatoriaus nustatyti šią paslaugą. Tada vėl įjunkite „Wi-Fi“ skambinimą Nustatymų skiltyje."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Užregistruokite pas operatorių"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"„%s“ „Wi-Fi“ skambinimas"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-lv/strings.xml b/core/res/res/values-mcc310-mnc200-lv/strings.xml
new file mode 100644
index 0000000..0471418
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-lv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Lai veiktu zvanus un sūtītu īsziņas Wi-Fi tīklā, vispirms lūdziet mobilo sakaru operatoram iestatīt šo pakalpojumu. Pēc tam iestatījumos vēlreiz ieslēdziet Wi-Fi zvanus."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Reģistrējieties pie sava mobilo sakaru operatora."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi zvani"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-mk-rMK/strings.xml b/core/res/res/values-mcc310-mnc200-mk-rMK/strings.xml
new file mode 100644
index 0000000..2582e2a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-mk-rMK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"За повикување и испраќање пораки преку Wi-Fi, прво побарајте од операторот да ви ја постави оваа услуга. Потоа повторно вклучете Повици преку Wi-Fi во Поставки."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Регистрирајте се кај операторот"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Повици преку Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ml-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-ml-rIN/strings.xml
new file mode 100644
index 0000000..6bdde6d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ml-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"വൈഫൈ വഴി കോളുകൾ വിളിക്കാനും സന്ദേശങ്ങൾ അയയ്ക്കാനും ആദ്യം നിങ്ങളുടെ കാരിയറോട് ഈ സേവനം സജ്ജമാക്കാൻ ആവശ്യപ്പെടുക. ക്രമീകരണത്തിൽ നിന്ന് വീണ്ടും വൈഫൈ കോളിംഗ് ഓണാക്കുക."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"നിങ്ങളുടെ കാരിയറിൽ രജിസ്റ്റർ ചെയ്യുക"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s വൈഫൈ കോളിംഗ്"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-mn-rMN/strings.xml b/core/res/res/values-mcc310-mnc200-mn-rMN/strings.xml
new file mode 100644
index 0000000..0d24a34
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-mn-rMN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi дуудлага хийх болон зурвас илгээх бол эхлээд оператор компаниасаа энэ төхөөрөмжийг тохируулахыг хүснэ үү. Дараа нь Тохиргооноос Wi-Fi дуудлага хийх үйлдлийг асаана уу."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Оператор компанидаа бүртгүүлэх"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi Дуудлага"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-mr-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-mr-rIN/strings.xml
new file mode 100644
index 0000000..fc89cfc
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-mr-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"वाय-फायवरून कॉल करण्यासाठी आणि संदेश पाठविण्यासाठी, प्रथम आपल्या वाहकास ही सेवा सेट करण्यास सांगा. नंतर सेटिंग्जमधून पुन्हा वाय-फाय कॉलिंग चालू करा."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"आपल्या वाहकासह नोंदणी करा"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s वाय-फाय कॉलिंग"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ms-rMY/strings.xml b/core/res/res/values-mcc310-mnc200-ms-rMY/strings.xml
new file mode 100644
index 0000000..f658b8d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ms-rMY/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Untuk membuat panggilan dan menghantar mesej melalui Wi-Fi, mula-mula minta pembawa anda menyediakan perkhidmatan ini. Kemudian, hidupkan panggilan Wi-Fi sekali lagi daripada Tetapan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Daftar dengan pembawa anda"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Panggilan Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-my-rMM/strings.xml b/core/res/res/values-mcc310-mnc200-my-rMM/strings.xml
new file mode 100644
index 0000000..b91c2d0
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-my-rMM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"ဝိုင်ဖိုင်ကိုအသုံးပြု၍ ဖုန်းခေါ်ဆိုရန်နှင့် စာပို့ရန်၊ ဤစက်ပစ္စည်းကို တပ်ဆင်ရန် သင့်အသုံးပြုသည့်မိုဘိုင်းဝန်ဆောင်မှုအား ဦးစွာမေးပါ။ ထို့နောက် ဆက်တင်များထဲမှ ဝိုင်ဖိုင်ခေါ်ဆိုမှုကို ဖွင့်ပါ။"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"သင့်အသုံးပြုသည့်မိုဘိုင်းဝန်ဆောင်မှုဖြင့် မှတ်ပုံတင်ရန်"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s ဝိုင်ဖိုင်ခေါ်ဆိုမှု"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-nb/strings.xml b/core/res/res/values-mcc310-mnc200-nb/strings.xml
new file mode 100644
index 0000000..353da10
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-nb/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Du må be operatøren din om å konfigurere denne tjenesten før du kan ringe og sende meldinger via Wi-Fi. Deretter slår du på Wi-Fi-anrop igjen fra innstillingene."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrer deg hos operatøren din"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi-anrop"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ne-rNP/strings.xml b/core/res/res/values-mcc310-mnc200-ne-rNP/strings.xml
new file mode 100644
index 0000000..5362798
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ne-rNP/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi मार्फत कल गर्न र सन्देशहरू पठाउन, सबभन्दा पहिले यो सेवा सेटअप गर्न तपाईँको वाहकलाई भन्नुहोस्। त्यसपछि फेरि सेटिङहरूबाट Wi-Fi कलिङ सक्रिय पार्नुहोस्।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"तपाईंको वाहकसँगै दर्ता गर्नुहोस्"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi कलिङ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-nl/strings.xml b/core/res/res/values-mcc310-mnc200-nl/strings.xml
new file mode 100644
index 0000000..22f8de2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-nl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Als je wilt bellen en berichten wilt verzenden via wifi, moet je eerst je provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via Instellingen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registreren bij je provider"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Bellen via wifi van %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-pa-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-pa-rIN/strings.xml
new file mode 100644
index 0000000..0083af3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-pa-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi \'ਤੇ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਲਈ, ਪਹਿਲਾਂ ਆਪਣੇ ਕੈਰੀਅਰ ਨੂੰ ਇਹ ਸੇਵਾ ਸੈੱਟ ਕਰਨ ਲਈ ਕਹੋ। ਫਿਰ ਸੈਟਿੰਗਾਂ ਤੋਂ Wi-Fi ਕਾਲਿੰਗ ਦੁਬਾਰਾ ਚਾਲੂ ਕਰੋ।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਰਜਿਸਟਰ ਕਰੋ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi ਕਾਲਿੰਗ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-pl/strings.xml b/core/res/res/values-mcc310-mnc200-pl/strings.xml
new file mode 100644
index 0000000..0ce774d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-pl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Aby dzwonić i wysyłać wiadomości przez Wi-Fi, poproś swojego operatora o skonfigurowanie tej usługi. Potem ponownie włącz połączenia przez Wi-Fi w Ustawieniach."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Zarejestruj u operatora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Połączenia przez Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc200-pt-rBR/strings.xml
new file mode 100644
index 0000000..23f3182
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-pt-rBR/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-pt-rPT/strings.xml b/core/res/res/values-mcc310-mnc200-pt-rPT/strings.xml
new file mode 100644
index 0000000..ff78c26
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-pt-rPT/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Para fazer chamadas e enviar mensagens por Wi-Fi, comece por pedir ao seu operador para configurar o serviço. Em seguida, nas Definições, ative novamente as Chamadas Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registar-se junto do seu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Chamadas Wi-Fi da %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-pt/strings.xml b/core/res/res/values-mcc310-mnc200-pt/strings.xml
new file mode 100644
index 0000000..23f3182
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-pt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ro/strings.xml b/core/res/res/values-mcc310-mnc200-ro/strings.xml
new file mode 100644
index 0000000..028a70b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ro/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Pentru a apela și a trimite mesaje prin Wi-Fi, mai întâi solicitați configurarea acestui serviciu la operator. Apoi, activați din nou apelarea prin Wi-Fi din Setări."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Înregistrați-vă la operatorul dvs."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Apelare prin Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ru/strings.xml b/core/res/res/values-mcc310-mnc200-ru/strings.xml
new file mode 100644
index 0000000..23a4c34
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ru/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Чтобы совершать звонки и отправлять сообщения по Wi-Fi, необходимо сначала обратиться к оператору связи и подключить эту услугу. После этого вы сможете снова выбрать этот параметр в настройках."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Укажите оператора и зарегистрируйтесь"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Звонки по Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-si-rLK/strings.xml b/core/res/res/values-mcc310-mnc200-si-rLK/strings.xml
new file mode 100644
index 0000000..f11ade3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-si-rLK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi හරහා ඇමතුම් සිදු කිරීමට සහ පණිවිඩ යැවීමට, පළමුව මෙම සේවාව පිහිටුවන ලෙස ඔබේ වාහකයෙන් ඉල්ලන්න. අනතුරුව සැකසීම් වෙතින් Wi-Fi ඇමතුම නැවත ක්රියාත්මක කරන්න."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"ඔබගේ වාහකය සමඟ ලියාපදිංචි වන්න"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi අමතමින්"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-sk/strings.xml b/core/res/res/values-mcc310-mnc200-sk/strings.xml
new file mode 100644
index 0000000..a64f9ec
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-sk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Ak chcete volať a odosielať správy prostredníctvom siete Wi-Fi, kontaktujte najskôr svojho operátora v súvislosti s nastavením tejto služby. Potom opäť zapnite v Nastaveniach volanie cez Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrujte sa so svojím operátorom"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Volanie cez Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-sl/strings.xml b/core/res/res/values-mcc310-mnc200-sl/strings.xml
new file mode 100644
index 0000000..06c3d22
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-sl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Če želite klicati ali pošiljati sporočila prek omrežja Wi-Fi, se najprej obrnite na operaterja, da nastavi to storitev. Nato v nastavitvah znova vklopite klicanje prek omrežja Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registracija pri operaterju"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Klicanje prek omrežja Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-sq-rAL/strings.xml b/core/res/res/values-mcc310-mnc200-sq-rAL/strings.xml
new file mode 100644
index 0000000..998d2fe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-sq-rAL/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Për të bërë telefonata dhe për të dërguar mesazhe me Wi-Fi, në fillim kërkoji operatorit celular ta konfigurojë këtë shërbim. Më pas aktivizo përsëri telefonatat me Wi-Fi, nga Cilësimet."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Regjistrohu me operatorin tënd celular"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Telefonatat me Wi-Fi nga %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-sr/strings.xml b/core/res/res/values-mcc310-mnc200-sr/strings.xml
new file mode 100644
index 0000000..20e9946
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-sr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Да бисте упућивали позиве и слали поруке преко Wi-Fi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко Wi-Fi-ја."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Региструјте се код мобилног оператера"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Wi-Fi позивање преко оператера %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-sv/strings.xml b/core/res/res/values-mcc310-mnc200-sv/strings.xml
new file mode 100644
index 0000000..3302b93
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-sv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Om du vill ringa samtal och skicka meddelanden via Wi-Fi ber du först operatören att konfigurera tjänsten. Därefter kan du aktivera Wi-Fi-samtal på nytt från Inställningar."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Registrera dig hos operatören"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi-samtal"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-sw/strings.xml b/core/res/res/values-mcc310-mnc200-sw/strings.xml
new file mode 100644
index 0000000..e53ca0b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-sw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Ili upige simu na kutuma ujumbe kupitia Wi-Fi, mwambie mtoa huduma wako asanidi huduma hii kwanza. Kisha uwashe tena upigaji simu kwa Wi-Fi kutoka kwenye Mipangilio."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Jisajili na mtoa huduma wako"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Upigaji Simu kwa Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ta-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-ta-rIN/strings.xml
new file mode 100644
index 0000000..49fecea
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ta-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"வைஃபை மூலம் அழைக்க மற்றும் செய்திகள் அனுப்ப, முதலில் மொபைல் நிறுவனத்திடம் இந்தச் சேவையை அமைக்குமாறு கேட்கவும். பிறகு அமைப்புகளில் மீண்டும் வைஃபை அழைப்பை இயக்கவும்."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"உங்கள் மொபைல் நிறுவனத்தில் பதிவுசெய்யவும்"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s வைஃபை அழைப்பு"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-te-rIN/strings.xml b/core/res/res/values-mcc310-mnc200-te-rIN/strings.xml
new file mode 100644
index 0000000..85f29ee
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-te-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fiలో కాల్లు చేయడానికి మరియు సందేశాలు పంపడానికి, ముందుగా ఈ సేవను సెటప్ చేయమని మీ క్యారియర్ను అడగండి. ఆపై సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ను మళ్లీ ఆన్ చేయండి."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"మీ క్యారియర్తో నమోదు చేయండి"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi కాలింగ్"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-th/strings.xml b/core/res/res/values-mcc310-mnc200-th/strings.xml
new file mode 100644
index 0000000..4758586
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-th/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"หากต้องการโทรออกและส่งข้อความผ่าน Wi-Fi โปรดสอบถามผู้ให้บริการของคุณก่อนเพื่อตั้งค่าบริการนี้ แล้วเปิดการโทรผ่าน Wi-Fi อีกครั้งจากการตั้งค่า"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"ลงทะเบียนกับผู้ให้บริการ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"การโทรผ่าน Wi-Fi ของ %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-tl/strings.xml b/core/res/res/values-mcc310-mnc200-tl/strings.xml
new file mode 100644
index 0000000..b7f41c7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-tl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Upang tumawag at magpadala ng mga mensahe sa pamamagitan ng Wi-Fi, hilingin muna sa iyong carrier na i-set up ang serbisyong ito. Pagkatapos ay muling i-on ang pagtawag sa Wi-Fi mula sa Mga Setting."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Magparehistro sa iyong carrier"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Pagtawag Gamit ang Wi-Fi ng %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-tr/strings.xml b/core/res/res/values-mcc310-mnc200-tr/strings.xml
new file mode 100644
index 0000000..e104eb9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-tr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Operatörünüze kaydolun"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Kablosuz Çağrı"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-uk/strings.xml b/core/res/res/values-mcc310-mnc200-uk/strings.xml
new file mode 100644
index 0000000..3780843
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-uk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Щоб телефонувати або надсилати повідомлення через Wi-Fi, спершу попросіть свого оператора налаштувати цю послугу. Після цього ввімкніть дзвінки через Wi-Fi у налаштуваннях."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Зареєструйтеся в оператора"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Дзвінок через Wi-Fi від оператора %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-ur-rPK/strings.xml b/core/res/res/values-mcc310-mnc200-ur-rPK/strings.xml
new file mode 100644
index 0000000..5a2de77
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-ur-rPK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi سے کالز کرنے اور پیغامات بھیجنے کیلئے، پہلے اپنے کیریئر سے اس سروس کو سیٹ اپ کرنے کیلئے کہیں۔ پھر ترتیبات سے دوبارہ Wi-Fi کالنگ آن کریں۔"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"اپنے کیریئر کے ساتھ رجسٹر کریں"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi کالنگ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-uz-rUZ/strings.xml b/core/res/res/values-mcc310-mnc200-uz-rUZ/strings.xml
new file mode 100644
index 0000000..5f0fefe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-uz-rUZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Wi-Fi orqali qo‘ng‘iroqlarni amalga oshirish va xabarlar bilan almashinish uchun uyali aloqa operatoringizdan ushbu xizmatni yoqib qo‘yishni so‘rashingiz lozim. Keyin sozlamalarda Wi-Fi qo‘ng‘irog‘i imkoniyatini yoqib olishingiz mumkin."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Mobil operatoringiz yordamida ro‘yxatdan o‘ting"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi qo‘ng‘iroqlar"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-vi/strings.xml b/core/res/res/values-mcc310-mnc200-vi/strings.xml
new file mode 100644
index 0000000..55b5c52
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-vi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Để gọi điện và gửi tin nhắn qua Wi-Fi, trước tiên hãy yêu cầu nhà cung cấp dịch vụ của bạn thiết lập dịch vụ này. Sau đó, bật lại gọi qua Wi-Fi từ Cài đặt."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Đăng ký với nhà cung cấp dịch vụ của bạn"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"Gọi điện qua Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc200-zh-rCN/strings.xml
new file mode 100644
index 0000000..6c2a8d9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-zh-rCN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"要通过 WLAN 打电话和发信息,请先让您的运营商开通此服务,然后再到“设置”中重新开启 WLAN 通话功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"向您的运营商注册"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s WLAN 通话功能"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
new file mode 100644
index 0000000..61bd0a1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-zh-rHK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"向您的流動網絡供應商註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-zh-rTW/strings.xml b/core/res/res/values-mcc310-mnc200-zh-rTW/strings.xml
new file mode 100644
index 0000000..9658757
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-zh-rTW/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"如要透過 Wi-Fi 撥打電話及傳送訊息,請先要求您的行動通訊業者開通這項服務,然後再到「設定」啟用 Wi-Fi 通話功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"向您的行動通訊業者註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200-zu/strings.xml b/core/res/res/values-mcc310-mnc200-zu/strings.xml
new file mode 100644
index 0000000..f4209a6
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200-zu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="9107329079910661798">"Ukuze wenze amakholi uphinde uthumele imilayezo nge-Wi-Fi, qala ucele inkampani yakho yenethiwekhi ukuthi isethe le divayisi. Bese uvula ukushaya kwe-Wi-Fi futhi kusukela kuzilungiselelo."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2841003137832065541">"Bhalisa ngenkampani yakho yenethiwekhi"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="6806975706640442517">"%s ukushaya kwe-Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-af/strings.xml b/core/res/res/values-mcc310-mnc210-af/strings.xml
new file mode 100644
index 0000000..7edb2bf
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-af/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Om oproepe te maak en boodskappe oor Wi-Fi te stuur, vra jou diensverskaffer eers om hierdie diens op te stel. Skakel Wi-Fi-oproepe dan weer in Instellings aan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registreer by jou diensverskaffer"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s-Wi-Fi-oproepe"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-am/strings.xml b/core/res/res/values-mcc310-mnc210-am/strings.xml
new file mode 100644
index 0000000..9eb9324
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-am/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"በWi-Fi ላይ ጥሪዎችን ለማድረግ እና መልዕክቶችን ለመላክ መጀመሪያ የአገልግሎት አቅራቢዎ ይህን አገልግሎት እንዲያዘጋጅልዎ ይጠይቁ። ከዚያ ከቅንብሮች ሆነው እንደገና የWi-Fi ጥሪን ያብሩ።"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"የአገልግሎት አቅራቢዎ ጋር ይመዝገቡ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"የ%s Wi-Fi ጥሪ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ar/strings.xml b/core/res/res/values-mcc310-mnc210-ar/strings.xml
new file mode 100644
index 0000000..9d5893d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ar/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"لإجراء مكالمات وإرسال رسائل عبر Wi-Fi، اطلب من مشغّل شبكة الجوّال أولاً إعداد هذه الخدمة، ثم شغّل الاتصال عبر Wi-Fi مرة أخرى من خلال الإعدادات."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"التسجيل لدى مشغّل شبكة الجوّال"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s جارٍ الاتصال عبر Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-az-rAZ/strings.xml b/core/res/res/values-mcc310-mnc210-az-rAZ/strings.xml
new file mode 100644
index 0000000..6a81835
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-az-rAZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi üzərindən zəng etmək və mesaj göndərmək üçün ilk öncə operatordan bu xidməti ayarlamağı tələb edin. Sonra Ayarlardan Wi-Fi çağrısını aktivləşdirin."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Operatorla qeydiyyatdan keçin"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi Zəngi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-b+sr+Latn/strings.xml b/core/res/res/values-mcc310-mnc210-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..dd43d61
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-b+sr+Latn/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Da biste upućivali pozive i slali poruke preko Wi-Fi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko Wi-Fi-ja."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrujte se kod mobilnog operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Wi-Fi pozivanje preko operatera %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-bg/strings.xml b/core/res/res/values-mcc310-mnc210-bg/strings.xml
new file mode 100644
index 0000000..b1ae65b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-bg/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"За да извършвате обаждания и да изпращате съобщения през Wi-Fi, първо, помолете оператора си да настрои тази услуга. След това включете отново функцията за обаждания през Wi-Fi от настройките."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Регистриране с оператора ви"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s – обаждания през Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-bn-rBD/strings.xml b/core/res/res/values-mcc310-mnc210-bn-rBD/strings.xml
new file mode 100644
index 0000000..b4d000d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-bn-rBD/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi এর মাধ্যমে কল করতে ও বার্তা পাঠাতে, প্রথমে আপনার পরিষেবা প্রদানকারীকে এই পরিষেবার সেট আপ করার বিষয়ে জিজ্ঞাসা করুন। তারপরে আবার সেটিংস থেকে Wi-Fi কলিং চালু করুন।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"আপনার পরিষেবা প্রদানকারীর সাথে নথিভুক্ত করুন"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi কলিং"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-bs-rBA/strings.xml b/core/res/res/values-mcc310-mnc210-bs-rBA/strings.xml
new file mode 100644
index 0000000..c9ee9f7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-bs-rBA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Da biste pozivali i slali poruke preko Wi-Fi-ja, prvo zatražite od operatera da postavi tu uslugu. Potom u Postavkama ponovo uključite Wi-Fi pozivanje."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrirajte se kod operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi pozivanje"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ca/strings.xml b/core/res/res/values-mcc310-mnc210-ca/strings.xml
new file mode 100644
index 0000000..aaa95cf
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ca/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Per fer trucades i enviar missatges per Wi-Fi, primer has de demanar a l\'operador de telefonia mòbil que configuri aquest servei. Després, torna a activar les trucades per Wi-Fi des de Configuració."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registra\'t amb el teu operador de telefonia mòbil"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Trucades per Wi-Fi amb %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-cs/strings.xml b/core/res/res/values-mcc310-mnc210-cs/strings.xml
new file mode 100644
index 0000000..70c727e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-cs/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Chcete-li volat a odesílat textové zprávy přes síť Wi-Fi, nejprve požádejte operátora, aby vám tuto službu nastavil. Poté volání přes Wi-Fi opět zapněte v Nastavení."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrace u operátora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Volání přes Wi-Fi: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-da/strings.xml b/core/res/res/values-mcc310-mnc210-da/strings.xml
new file mode 100644
index 0000000..e50fec0
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-da/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Før du kan foretage opkald og sende beskeder via Wi-Fi, skal du anmode dit mobilselskab om at konfigurere denne tjeneste. Du skal derefter slå Wi-Fi-opkald til igen fra Indstillinger."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrer dig hos dit mobilselskab"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi-opkald"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-de/strings.xml b/core/res/res/values-mcc310-mnc210-de/strings.xml
new file mode 100644
index 0000000..40be9d5
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-de/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Um über WLAN telefonieren und Nachrichten senden zu können, bitte zuerst deinen Mobilfunkanbieter, diesen Dienst einzurichten. Aktiviere die Option \"Anrufe über WLAN\" dann noch einmal über die Einstellungen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registriere dich bei deinem Mobilfunkanbieter"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s-WLAN-Anrufe"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-el/strings.xml b/core/res/res/values-mcc310-mnc210-el/strings.xml
new file mode 100644
index 0000000..18ebf12
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-el/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Για να κάνετε κλήσεις και να στέλνετε μηνύματα μέσω Wi-Fi, ζητήστε πρώτα από την εταιρεία κινητής τηλεφωνίας που χρησιμοποιείτε να ρυθμίσει την υπηρεσία. Στη συνέχεια, ενεργοποιήστε ξανά τη λειτουργία Κλήσης Wi-Fi από τις Ρυθμίσεις."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Εγγραφείτε μέσω της εταιρείας κινητής τηλεφωνίας"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Κλήση Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-en-rAU/strings.xml b/core/res/res/values-mcc310-mnc210-en-rAU/strings.xml
new file mode 100644
index 0000000..ff31e92
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-en-rAU/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-en-rGB/strings.xml b/core/res/res/values-mcc310-mnc210-en-rGB/strings.xml
new file mode 100644
index 0000000..ff31e92
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-en-rGB/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-en-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-en-rIN/strings.xml
new file mode 100644
index 0000000..ff31e92
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-en-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Register with your operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi Calling"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-es-rUS/strings.xml b/core/res/res/values-mcc310-mnc210-es-rUS/strings.xml
new file mode 100644
index 0000000..86cbdf1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-es-rUS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Para realizar llamadas y enviar mensajes con Wi-Fi, primero solicítale al proveedor que instale el servicio. Luego, vuelve a activar Llamada con Wi-Fi en Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Regístrate con tu proveedor"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Llamada con Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-es/strings.xml b/core/res/res/values-mcc310-mnc210-es/strings.xml
new file mode 100644
index 0000000..8fe5eba
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-es/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Para hacer llamadas y enviar mensajes por Wi-Fi, solicita a tu operador que configure este servicio y, cuando lo haga, vuelve a activar las llamadas por Wi-Fi en Ajustes."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Regístrate con tu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Llamada por Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-et-rEE/strings.xml b/core/res/res/values-mcc310-mnc210-et-rEE/strings.xml
new file mode 100644
index 0000000..3da1866
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-et-rEE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"WiFi-võrgu kaudu helistamiseks ja sõnumite saatmiseks paluge operaatoril esmalt see teenus seadistada. Seejärel lülitage WiFi-kõned menüüs Seaded uuesti sisse."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registreeruge operaatori juures"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Operaatori %s WiFi-kõned"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-eu-rES/strings.xml b/core/res/res/values-mcc310-mnc210-eu-rES/strings.xml
new file mode 100644
index 0000000..b033231
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-eu-rES/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wi-Fi bidezko deiak Ezarpenak atalean."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Erregistratu operadorearekin"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi bidezko deiak"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-fa/strings.xml b/core/res/res/values-mcc310-mnc210-fa/strings.xml
new file mode 100644
index 0000000..8cb15f7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-fa/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"برای برقراری تماس و ارسال پیام از طریق Wi-Fi، ابتدا از شرکت مخابراتیتان درخواست کنید این سرویس را راهاندازی کند. سپس دوباره در «تنظیمات»، تماس از طریق Wi-Fi را روشن کنید."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"از طریق شرکت مخابراتیتان ثبتنام کنید"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"تماس از طریق %s Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-fi/strings.xml b/core/res/res/values-mcc310-mnc210-fi/strings.xml
new file mode 100644
index 0000000..b081845
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-fi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Jos haluat soittaa puheluita ja lähettää viestejä Wi-Fin kautta, pyydä ensin operaattoriasi ottamaan tämä palvelu käyttöön. Ota sitten Wi-Fi-puhelut käyttöön asetuksissa."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Rekisteröidy operaattorisi asiakkaaksi."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Wi-Fi-puhelut: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-fr-rCA/strings.xml b/core/res/res/values-mcc310-mnc210-fr-rCA/strings.xml
new file mode 100644
index 0000000..43d9d93
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-fr-rCA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Pour effectuer des appels et envoyer des messages par Wi-Fi, demandez tout d\'abord à votre fournisseur de services de configurer ce service. Réactivez ensuite les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Inscrivez-vous auprès de votre fournisseur de services"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-fr/strings.xml b/core/res/res/values-mcc310-mnc210-fr/strings.xml
new file mode 100644
index 0000000..6b3cacb
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-fr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Pour passer des appels et envoyer des messages via le Wi-Fi, demandez d\'abord à votre opérateur de configurer ce service. Ensuite, réactivez les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Inscrivez-vous auprès de votre opérateur."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-gl-rES/strings.xml b/core/res/res/values-mcc310-mnc210-gl-rES/strings.xml
new file mode 100644
index 0000000..fe2a326
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-gl-rES/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Para facer chamadas e enviar mensaxes a través da wifi, primeiro pídelle ao teu operador que configure este servizo. A continuación, activa de novo as chamadas por wifi en Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Rexístrate co teu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Chamadas por wifi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-gu-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-gu-rIN/strings.xml
new file mode 100644
index 0000000..ccb4be4
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-gu-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi પર કૉલ્સ કરવા અને સંદેશા મોકલવા માટે, પહેલા તમારા કેરીઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગ્સમાંથી Wi-Fi કૉલિંગ ચાલુ કરો."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"તમારા કેરીઅર સાથે નોંધણી કરો"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi કૉલિંગ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-hi/strings.xml b/core/res/res/values-mcc310-mnc210-hi/strings.xml
new file mode 100644
index 0000000..b560f04
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-hi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"वाई-फ़ाई से कॉल करने और संदेश भेजने के लिए, सबसे पहले अपने वाहक से इस सेवा को सेट करने के लिए कहें. उसके बाद सेटिंग से पुन: वाई-फ़ाई कॉलिंग चालू करें."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"अपने वाहक के साथ पंजीकृत करें"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s वाई-फ़ाई कॉलिंग"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-hr/strings.xml b/core/res/res/values-mcc310-mnc210-hr/strings.xml
new file mode 100644
index 0000000..caf5df9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-hr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Da biste telefonirali i slali poruke putem Wi-Fi-ja, od mobilnog operatera morate tražiti da vam postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozive u postavkama."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrirajte se kod mobilnog operatera"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi pozivi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-hu/strings.xml b/core/res/res/values-mcc310-mnc210-hu/strings.xml
new file mode 100644
index 0000000..f7465a4
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-hu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Ha Wi-Fin szeretne telefonálni és üzenetet küldeni, kérje meg szolgáltatóját, hogy állítsa be ezt a szolgáltatást. Ezután a Beállítások menüben kapcsolhatja be újra a Wi-Fi-hívást."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Regisztráljon szolgáltatójánál"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi-hívás"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-hy-rAM/strings.xml b/core/res/res/values-mcc310-mnc210-hy-rAM/strings.xml
new file mode 100644
index 0000000..69623ff
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-hy-rAM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi-ի միջոցով զանգեր կատարելու և հաղորդագրություններ ուղարկելու համար նախ դիմեք ձեր օպերատորին՝ ծառայությունը կարգավորելու համար: Ապա նորից միացրեք Wi-Fi զանգերը Կարգավորումներում:"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Գրանցվեք օպերատորի մոտ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi զանգեր"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-in/strings.xml b/core/res/res/values-mcc310-mnc210-in/strings.xml
new file mode 100644
index 0000000..264bb51
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-in/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Untuk melakukan panggilan telepon dan mengirim pesan melalui Wi-Fi, terlebih dahulu minta operator untuk menyiapkan layanan ini. Lalu, aktifkan lagi panggilan telepon Wi-Fi dari Setelan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Harap daftarkan ke operator"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Panggilan Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-is-rIS/strings.xml b/core/res/res/values-mcc310-mnc210-is-rIS/strings.xml
new file mode 100644
index 0000000..f2433a0
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-is-rIS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Til að hringja og senda skilaboð yfir Wi-Fi þarftu fyrst að biðja símafyrirtækið þitt um að setja þá þjónustu upp. Kveiktu síðan á Wi-Fi símtölum í stillingunum."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Skráðu þig hjá símafyrirtækinu"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi símtöl"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-it/strings.xml b/core/res/res/values-mcc310-mnc210-it/strings.xml
new file mode 100644
index 0000000..a307e4a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-it/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Per poter effettuare chiamate e inviare messaggi tramite Wi-Fi, devi chiedere all\'operatore di attivare il servizio. Dopodiché, riattiva le chiamate Wi-Fi dalle Impostazioni."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrati con il tuo operatore"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Chiamate Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-iw/strings.xml b/core/res/res/values-mcc310-mnc210-iw/strings.xml
new file mode 100644
index 0000000..4ecc513
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-iw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"כדי להתקשר ולשלוח הודעות ברשת Wi-Fi, תחילה יש לבקש מהספק להגדיר את השירות. לאחר מכן, יש להפעיל שוב שיחות Wi-Fi ב\'הגדרות\'."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"הירשם אצל הספק"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"שיחות Wi-Fi של %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ja/strings.xml b/core/res/res/values-mcc310-mnc210-ja/strings.xml
new file mode 100644
index 0000000..4cf6215
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ja/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi 経由で音声通話の発信やメッセージの送信を行うには、携帯通信会社に Wi-Fi サービスを申し込んだ上で、設定画面で Wi-Fi 発信を再度 ON にしてください。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"携帯通信会社に登録してください"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Wi-Fi 通話(%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ka-rGE/strings.xml b/core/res/res/values-mcc310-mnc210-ka-rGE/strings.xml
new file mode 100644
index 0000000..d12328c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ka-rGE/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi-ს მეშვეობით ზარების განსახორციელებლად ან შეტყობინებების გასაგზავნად, პირველ რიგში, ამ სერვისის გააქტიურება თქვენს ოპერატორს უნდა თხოვოთ. შემდეგ ხელახლა ჩართეთ Wi-Fi დარეკვა პარამეტრებიდან."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"დარეგისტრირდით თქვენი ოპერატორის მეშვეობით"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Wi-Fi დარეკვა (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-kk-rKZ/strings.xml b/core/res/res/values-mcc310-mnc210-kk-rKZ/strings.xml
new file mode 100644
index 0000000..cc6b022
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-kk-rKZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi арқылы қоңырау шалу және хабарларды жіберу үшін алдымен жабдықтаушыңыздан осы қызметті орнатуды сұраңыз. Содан кейін \"Параметрлер\" тармағында Wi-Fi қоңырауларын қосыңыз."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Оператор арқылы тіркелу"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi қоңыраулары"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-km-rKH/strings.xml b/core/res/res/values-mcc310-mnc210-km-rKH/strings.xml
new file mode 100644
index 0000000..7a19737
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-km-rKH/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"ដើម្បីធ្វើការហៅ និងផ្ញើសារតាម Wi-Fi ដំបូងឡើយអ្នកត្រូវស្នើឲ្យក្រុមហ៊ុនរបស់អ្នកដំឡើងសេវាកម្មនេះសិន។ បន្ទាប់មកបើកការហៅតាម Wi-Fi ម្តងទៀតចេញពីការកំណត់។"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"ចុះឈ្មោះជាមួយក្រុមហ៊ុនរបស់អ្នក"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"ការហៅតាមរយៈ Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-kn-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-kn-rIN/strings.xml
new file mode 100644
index 0000000..037ccda
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-kn-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"ವೈ-ಫೈ ಬಳಸಿಕೊಂಡು ಕರೆ ಮಾಡಲು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, ಮೊದಲು ಈ ಸಾಧನವನ್ನು ಹೊಂದಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಕೇಳಿ. ತದನಂತರ ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಮತ್ತೆ ವೈ-ಫೈ ಆನ್ ಮಾಡಿ."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"ನಿಮ್ಮ ವಾಹಕದಲ್ಲಿ ನೋಂದಾಯಿಸಿಕೊಳ್ಳಿ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ko/strings.xml b/core/res/res/values-mcc310-mnc210-ko/strings.xml
new file mode 100644
index 0000000..c278352
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ko/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi를 사용하여 전화를 걸고 메시지를 보내려면 먼저 이동통신사에 문의하여 이 기능을 설정해야 합니다. 그런 다음 설정에서 Wi-Fi 통화를 사용 설정하시기 바랍니다."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"이동통신사에 등록"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi 통화"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ky-rKG/strings.xml b/core/res/res/values-mcc310-mnc210-ky-rKG/strings.xml
new file mode 100644
index 0000000..1dce9c4
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ky-rKG/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi аркылуу чалууларды аткарып жана билдирүүлөрдү жөнөтүү үчүн адегенде операторуңуздан бул кызматты орнотушун сураныңыз. Андан соң, Жөндөөлөрдөн Wi-Fi чалууну кайра күйгүзүңүз."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Операторуңузга катталыңыз"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi чалуу"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-lo-rLA/strings.xml b/core/res/res/values-mcc310-mnc210-lo-rLA/strings.xml
new file mode 100644
index 0000000..d54c935
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-lo-rLA/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"ເພື່ອໂທ ແລະ ສົ່ງຂໍ້ຄວາມຜ່ານ Wi-Fi, ໃຫ້ແຈ້ງຜູ້ໃຫ້ບໍລິການເຄືອຂ່າຍຂອງທ່ານເພື່ອຕັ້ງບໍລິການນີ້ກ່ອນ. ຈາກນັ້ນ ເປີດການໂທ Wi-Fi ອີກເທື່ອໜຶ່ງຈາກການຕັ້ງຄ່າ."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"ລົງທະບຽນນໍາຜູ້ໃຫ້ບໍລິການເຄືອຂ່າຍຂອງທ່ານ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"ການໂທ %s Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-lt/strings.xml b/core/res/res/values-mcc310-mnc210-lt/strings.xml
new file mode 100644
index 0000000..a8c2a56
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-lt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Jei norite skambinti ir siųsti pranešimus naudodami „Wi-Fi“, pirmiausia paprašykite operatoriaus nustatyti šią paslaugą. Tada vėl įjunkite „Wi-Fi“ skambinimą Nustatymų skiltyje."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Užregistruokite pas operatorių"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"„%s“ „Wi-Fi“ skambinimas"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-lv/strings.xml b/core/res/res/values-mcc310-mnc210-lv/strings.xml
new file mode 100644
index 0000000..566a4f1
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-lv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Lai veiktu zvanus un sūtītu īsziņas Wi-Fi tīklā, vispirms lūdziet mobilo sakaru operatoram iestatīt šo pakalpojumu. Pēc tam iestatījumos vēlreiz ieslēdziet Wi-Fi zvanus."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Reģistrējieties pie sava mobilo sakaru operatora."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi zvani"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-mk-rMK/strings.xml b/core/res/res/values-mcc310-mnc210-mk-rMK/strings.xml
new file mode 100644
index 0000000..f47bbde
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-mk-rMK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"За повикување и испраќање пораки преку Wi-Fi, прво побарајте од операторот да ви ја постави оваа услуга. Потоа повторно вклучете Повици преку Wi-Fi во Поставки."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Регистрирајте се кај операторот"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Повици преку Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ml-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-ml-rIN/strings.xml
new file mode 100644
index 0000000..20bf1eb
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ml-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"വൈഫൈ വഴി കോളുകൾ വിളിക്കാനും സന്ദേശങ്ങൾ അയയ്ക്കാനും ആദ്യം നിങ്ങളുടെ കാരിയറോട് ഈ സേവനം സജ്ജമാക്കാൻ ആവശ്യപ്പെടുക. ക്രമീകരണത്തിൽ നിന്ന് വീണ്ടും വൈഫൈ കോളിംഗ് ഓണാക്കുക."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"നിങ്ങളുടെ കാരിയറിൽ രജിസ്റ്റർ ചെയ്യുക"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s വൈഫൈ കോളിംഗ്"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-mn-rMN/strings.xml b/core/res/res/values-mcc310-mnc210-mn-rMN/strings.xml
new file mode 100644
index 0000000..32e7c112
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-mn-rMN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi дуудлага хийх болон зурвас илгээх бол эхлээд оператор компаниасаа энэ төхөөрөмжийг тохируулахыг хүснэ үү. Дараа нь Тохиргооноос Wi-Fi дуудлага хийх үйлдлийг асаана уу."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Оператор компанидаа бүртгүүлэх"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi Дуудлага"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-mr-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-mr-rIN/strings.xml
new file mode 100644
index 0000000..1d29585
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-mr-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"वाय-फायवरून कॉल करण्यासाठी आणि संदेश पाठविण्यासाठी, प्रथम आपल्या वाहकास ही सेवा सेट करण्यास सांगा. नंतर सेटिंग्जमधून पुन्हा वाय-फाय कॉलिंग चालू करा."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"आपल्या वाहकासह नोंदणी करा"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s वाय-फाय कॉलिंग"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ms-rMY/strings.xml b/core/res/res/values-mcc310-mnc210-ms-rMY/strings.xml
new file mode 100644
index 0000000..961f9cf
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ms-rMY/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Untuk membuat panggilan dan menghantar mesej melalui Wi-Fi, mula-mula minta pembawa anda menyediakan perkhidmatan ini. Kemudian, hidupkan panggilan Wi-Fi sekali lagi daripada Tetapan."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Daftar dengan pembawa anda"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Panggilan Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-my-rMM/strings.xml b/core/res/res/values-mcc310-mnc210-my-rMM/strings.xml
new file mode 100644
index 0000000..078d196
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-my-rMM/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"ဝိုင်ဖိုင်ကိုအသုံးပြု၍ ဖုန်းခေါ်ဆိုရန်နှင့် စာပို့ရန်၊ ဤစက်ပစ္စည်းကို တပ်ဆင်ရန် သင့်အသုံးပြုသည့်မိုဘိုင်းဝန်ဆောင်မှုအား ဦးစွာမေးပါ။ ထို့နောက် ဆက်တင်များထဲမှ ဝိုင်ဖိုင်ခေါ်ဆိုမှုကို ဖွင့်ပါ။"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"သင့်အသုံးပြုသည့်မိုဘိုင်းဝန်ဆောင်မှုဖြင့် မှတ်ပုံတင်ရန်"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s ဝိုင်ဖိုင်ခေါ်ဆိုမှု"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-nb/strings.xml b/core/res/res/values-mcc310-mnc210-nb/strings.xml
new file mode 100644
index 0000000..36adfa2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-nb/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Du må be operatøren din om å konfigurere denne tjenesten før du kan ringe og sende meldinger via Wi-Fi. Deretter slår du på Wi-Fi-anrop igjen fra innstillingene."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrer deg hos operatøren din"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi-anrop"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ne-rNP/strings.xml b/core/res/res/values-mcc310-mnc210-ne-rNP/strings.xml
new file mode 100644
index 0000000..9a66faa
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ne-rNP/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi मार्फत कल गर्न र सन्देशहरू पठाउन, सबभन्दा पहिला यो सेवा सेटअप गर्न तपाईँको वाहकलाई भन्नुहोस्। त्यसपछि फेरि सेटिङहरूबाट Wi-Fi कलिङ सक्रिय पार्नुहोस्।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"आफ्नो वाहकसँगै दर्ता गर्नुहोस्"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi कलिङ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-nl/strings.xml b/core/res/res/values-mcc310-mnc210-nl/strings.xml
new file mode 100644
index 0000000..d90df12
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-nl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Als je wilt bellen en berichten wilt verzenden via wifi, moet je eerst je provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via Instellingen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registreren bij je provider"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Bellen via wifi van %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-pa-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-pa-rIN/strings.xml
new file mode 100644
index 0000000..39bdd6405
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-pa-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi \'ਤੇ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਲਈ, ਪਹਿਲਾਂ ਆਪਣੇ ਕੈਰੀਅਰ ਨੂੰ ਇਹ ਸੇਵਾ ਸੈੱਟ ਕਰਨ ਲਈ ਕਹੋ। ਫਿਰ ਸੈਟਿੰਗਾਂ ਤੋਂ Wi-Fi ਕਾਲਿੰਗ ਦੁਬਾਰਾ ਚਾਲੂ ਕਰੋ।"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਰਜਿਸਟਰ ਕਰੋ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi ਕਾਲਿੰਗ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-pl/strings.xml b/core/res/res/values-mcc310-mnc210-pl/strings.xml
new file mode 100644
index 0000000..f6865cf
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-pl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Aby dzwonić i wysyłać wiadomości przez Wi-Fi, poproś swojego operatora o skonfigurowanie tej usługi. Potem ponownie włącz połączenia przez Wi-Fi w Ustawieniach."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Zarejestruj u operatora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Połączenia przez Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc210-pt-rBR/strings.xml
new file mode 100644
index 0000000..90561a2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-pt-rBR/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-pt-rPT/strings.xml b/core/res/res/values-mcc310-mnc210-pt-rPT/strings.xml
new file mode 100644
index 0000000..c5ee6d3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-pt-rPT/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Para fazer chamadas e enviar mensagens por Wi-Fi, comece por pedir ao seu operador para configurar o serviço. Em seguida, nas Definições, ative novamente as Chamadas Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registar-se junto do seu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Chamadas Wi-Fi da %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-pt/strings.xml b/core/res/res/values-mcc310-mnc210-pt/strings.xml
new file mode 100644
index 0000000..90561a2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-pt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ro/strings.xml b/core/res/res/values-mcc310-mnc210-ro/strings.xml
new file mode 100644
index 0000000..90a59c0a
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ro/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Pentru a apela și a trimite mesaje prin Wi-Fi, mai întâi solicitați configurarea acestui serviciu la operator. Apoi, activați din nou apelarea prin Wi-Fi din Setări."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Înregistrați-vă la operatorul dvs."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Apelare prin Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ru/strings.xml b/core/res/res/values-mcc310-mnc210-ru/strings.xml
new file mode 100644
index 0000000..18cd9e0
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ru/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Чтобы совершать звонки и отправлять сообщения по Wi-Fi, необходимо сначала обратиться к оператору связи и подключить эту услугу. После этого вы сможете снова выбрать этот параметр в настройках."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Укажите оператора и зарегистрируйтесь"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Звонки по Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-si-rLK/strings.xml b/core/res/res/values-mcc310-mnc210-si-rLK/strings.xml
new file mode 100644
index 0000000..bc4e9ee
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-si-rLK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi හරහා ඇමතුම් සිදු කිරීමට සහ පණිවිඩ යැවීමට, පළමුව මෙම සේවාව පිහිටුවන ලෙස ඔබේ වාහකයෙන් ඉල්ලන්න. අනතුරුව සැකසීම් වෙතින් Wi-Fi ඇමතුම නැවත ක්රියාත්මක කරන්න."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"ඔබගේ වාහකය සමඟ ලියාපදිංචි වන්න"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi අමතමින්"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-sk/strings.xml b/core/res/res/values-mcc310-mnc210-sk/strings.xml
new file mode 100644
index 0000000..7fe4d5e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-sk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Ak chcete volať a odosielať správy prostredníctvom siete Wi-Fi, kontaktujte najskôr svojho operátora v súvislosti s nastavením tejto služby. Potom opäť zapnite v Nastaveniach volanie cez Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrujte sa so svojím operátorom"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Volanie cez Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-sl/strings.xml b/core/res/res/values-mcc310-mnc210-sl/strings.xml
new file mode 100644
index 0000000..f1938a7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-sl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Če želite klicati ali pošiljati sporočila prek omrežja Wi-Fi, se najprej obrnite na operaterja, da nastavi to storitev. Nato v nastavitvah znova vklopite klicanje prek omrežja Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registracija pri operaterju"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Klicanje prek omrežja Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-sq-rAL/strings.xml b/core/res/res/values-mcc310-mnc210-sq-rAL/strings.xml
new file mode 100644
index 0000000..bcaa4a2
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-sq-rAL/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Për të bërë telefonata dhe për të dërguar mesazhe me Wi-Fi, në fillim kërkoji operatorit celular ta konfigurojë këtë shërbim. Më pas aktivizo përsëri telefonatat me Wi-Fi, nga Cilësimet."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Regjistrohu me operatorin tënd celular"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Telefonatat me Wi-Fi nga %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-sr/strings.xml b/core/res/res/values-mcc310-mnc210-sr/strings.xml
new file mode 100644
index 0000000..324e5c9
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-sr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Да бисте упућивали позиве и слали поруке преко Wi-Fi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко Wi-Fi-ја."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Региструјте се код мобилног оператера"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Wi-Fi позивање преко оператера %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-sv/strings.xml b/core/res/res/values-mcc310-mnc210-sv/strings.xml
new file mode 100644
index 0000000..bd329f3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-sv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Om du vill ringa samtal och skicka meddelanden via Wi-Fi ber du först operatören att konfigurera tjänsten. Därefter kan du aktivera Wi-Fi-samtal på nytt från Inställningar."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Registrera dig hos operatören"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi-samtal"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-sw/strings.xml b/core/res/res/values-mcc310-mnc210-sw/strings.xml
new file mode 100644
index 0000000..616c3bd
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-sw/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Ili upige simu na kutuma ujumbe kupitia Wi-Fi, mwambie mtoa huduma wako asanidi huduma hii kwanza. Kisha uwashe tena upigaji simu kwa Wi-Fi kutoka kwenye Mipangilio."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Jisajili na mtoa huduma wako"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Upigaji Simu kwa Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ta-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-ta-rIN/strings.xml
new file mode 100644
index 0000000..411b8ef
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ta-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"வைஃபை மூலம் அழைக்க மற்றும் செய்திகள் அனுப்ப, முதலில் மொபைல் நிறுவனத்திடம் இந்தச் சேவையை அமைக்குமாறு கேட்கவும். பிறகு அமைப்புகளில் மீண்டும் வைஃபை அழைப்பை இயக்கவும்."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"உங்கள் மொபைல் நிறுவனத்தில் பதிவுசெய்யவும்"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s வைஃபை அழைப்பு"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-te-rIN/strings.xml b/core/res/res/values-mcc310-mnc210-te-rIN/strings.xml
new file mode 100644
index 0000000..d3141d8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-te-rIN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fiలో కాల్లు చేయడానికి మరియు సందేశాలు పంపడానికి, ముందుగా ఈ సేవను సెటప్ చేయమని మీ క్యారియర్ను అడగండి. ఆపై సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ను మళ్లీ ఆన్ చేయండి."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"మీ క్యారియర్తో నమోదు చేయండి"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi కాలింగ్"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-th/strings.xml b/core/res/res/values-mcc310-mnc210-th/strings.xml
new file mode 100644
index 0000000..a7929ee
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-th/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"หากต้องการโทรออกและส่งข้อความผ่าน Wi-Fi โปรดสอบถามผู้ให้บริการของคุณก่อนเพื่อตั้งค่าบริการนี้ แล้วเปิดการโทรผ่าน Wi-Fi อีกครั้งจากการตั้งค่า"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"ลงทะเบียนกับผู้ให้บริการ"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"การโทรผ่าน Wi-Fi ของ %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-tl/strings.xml b/core/res/res/values-mcc310-mnc210-tl/strings.xml
new file mode 100644
index 0000000..2598e22
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-tl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Upang tumawag at magpadala ng mga mensahe sa pamamagitan ng Wi-Fi, hilingin muna sa iyong carrier na i-set up ang serbisyong ito. Pagkatapos ay muling i-on ang pagtawag sa Wi-Fi mula sa Mga Setting."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Magparehistro sa iyong carrier"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Pagtawag Gamit ang Wi-Fi ng %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-tr/strings.xml b/core/res/res/values-mcc310-mnc210-tr/strings.xml
new file mode 100644
index 0000000..1da0b1f
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-tr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Operatörünüze kaydolun"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Kablosuz Çağrı"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-uk/strings.xml b/core/res/res/values-mcc310-mnc210-uk/strings.xml
new file mode 100644
index 0000000..37b445d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-uk/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Щоб телефонувати або надсилати повідомлення через Wi-Fi, спершу попросіть свого оператора налаштувати цю послугу. Після цього ввімкніть дзвінки через Wi-Fi у налаштуваннях."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Зареєструйтеся в оператора"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Дзвінок через Wi-Fi від оператора %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-ur-rPK/strings.xml b/core/res/res/values-mcc310-mnc210-ur-rPK/strings.xml
new file mode 100644
index 0000000..150a0bd
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-ur-rPK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi سے کالز کرنے اور پیغامات بھیجنے کیلئے، پہلے اپنے کیریئر سے اس سروس کو سیٹ اپ کرنے کیلئے کہیں۔ پھر ترتیبات سے دوبارہ Wi-Fi کالنگ آن کریں۔"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"اپنے کیریئر کے ساتھ رجسٹر کریں"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi کالنگ"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-uz-rUZ/strings.xml b/core/res/res/values-mcc310-mnc210-uz-rUZ/strings.xml
new file mode 100644
index 0000000..6e9b7e0
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-uz-rUZ/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Wi-Fi orqali qo‘ng‘iroqlarni amalga oshirish va xabarlar bilan almashinish uchun uyali aloqa operatoringizdan ushbu xizmatni yoqib qo‘yishni so‘rashingiz lozim. Keyin sozlamalarda Wi-Fi qo‘ng‘irog‘i imkoniyatini yoqib olishingiz mumkin."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Mobil operatoringiz yordamida ro‘yxatdan o‘ting"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi qo‘ng‘iroqlar"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-vi/strings.xml b/core/res/res/values-mcc310-mnc210-vi/strings.xml
new file mode 100644
index 0000000..302c51d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-vi/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Để gọi điện và gửi tin nhắn qua Wi-Fi, trước tiên hãy yêu cầu nhà cung cấp dịch vụ của bạn thiết lập dịch vụ này. Sau đó, bật lại gọi qua Wi-Fi từ Cài đặt."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Đăng ký với nhà cung cấp dịch vụ của bạn"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"Gọi điện qua Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc210-zh-rCN/strings.xml
new file mode 100644
index 0000000..3562239
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-zh-rCN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"要通过 WLAN 打电话和发信息,请先让您的运营商开通此服务,然后再到“设置”中重新开启 WLAN 通话功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"向您的运营商注册"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s WLAN 通话功能"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
new file mode 100644
index 0000000..f890edf
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-zh-rHK/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟 [Wi-Fi 通話]。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"向您的流動網絡供應商註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-zh-rTW/strings.xml b/core/res/res/values-mcc310-mnc210-zh-rTW/strings.xml
new file mode 100644
index 0000000..13ef816
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-zh-rTW/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"如要透過 Wi-Fi 撥打電話及傳送訊息,請先要求您的行動通訊業者開通這項服務,然後再到「設定」啟用 Wi-Fi 通話功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"向您的行動通訊業者註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210-zu/strings.xml b/core/res/res/values-mcc310-mnc210-zu/strings.xml
new file mode 100644
index 0000000..cde0a8c
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210-zu/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="5217754856196352581">"Ukuze wenze amakholi uphinde uthumele imilayezo nge-Wi-Fi, qala ucele inkampani yakho yenethiwekhi ukuthi isethe le divayisi. Bese uvula ukushaya kwe-Wi-Fi futhi kusukela kuzilungiselelo."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="4688475512286389971">"Bhalisa ngenkampani yakho yenethiwekhi"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="5475635312889002673">"%s ukushaya kwe-Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-ar/strings.xml b/core/res/res/values-mcc310-mnc220-ar/strings.xml
new file mode 100644
index 0000000..229c9ce
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-ar/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"لإجراء مكالمات وإرسال رسائل عبر Wi-Fi، اطلب من مشغّل شبكة الجوّال أولاً إعداد هذه الخدمة، ثم شغّل الاتصال عبر Wi-Fi مرة أخرى من خلال الإعدادات."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"التسجيل لدى مشغّل شبكة الجوّال"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s جارٍ الاتصال عبر Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-cs/strings.xml b/core/res/res/values-mcc310-mnc220-cs/strings.xml
new file mode 100644
index 0000000..776c183
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-cs/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Chcete-li volat a odesílat textové zprávy přes síť Wi-Fi, nejprve požádejte operátora, aby vám tuto službu nastavil. Poté volání přes Wi-Fi opět zapněte v Nastavení."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registrace u operátora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Volání přes Wi-Fi: %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-da/strings.xml b/core/res/res/values-mcc310-mnc220-da/strings.xml
new file mode 100644
index 0000000..de6b124
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-da/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Før du kan foretage opkald og sende beskeder via Wi-Fi, skal du anmode dit mobilselskab om at konfigurere denne tjeneste. Du skal derefter slå Wi-Fi-opkald til igen fra Indstillinger."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registrer dig hos dit mobilselskab"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Wi-Fi-opkald"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-de/strings.xml b/core/res/res/values-mcc310-mnc220-de/strings.xml
new file mode 100644
index 0000000..b7cdd38
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-de/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Um über WLAN telefonieren und Nachrichten senden zu können, bitte zuerst deinen Mobilfunkanbieter, diesen Dienst einzurichten. Aktiviere die Option \"Anrufe über WLAN\" dann noch einmal über die Einstellungen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registriere dich bei deinem Mobilfunkanbieter"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s-WLAN-Anrufe"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-el/strings.xml b/core/res/res/values-mcc310-mnc220-el/strings.xml
new file mode 100644
index 0000000..940e23d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-el/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Για να κάνετε κλήσεις και να στέλνετε μηνύματα μέσω Wi-Fi, ζητήστε πρώτα από την εταιρεία κινητής τηλεφωνίας να ρυθμίσει την υπηρεσία. Στη συνέχεια, ενεργοποιήστε ξανά τη λειτουργία Κλήσης Wi-Fi από τις Ρυθμίσεις."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Εγγραφείτε μέσω της εταιρείας κινητής τηλεφωνίας"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Κλήση Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-es-rUS/strings.xml b/core/res/res/values-mcc310-mnc220-es-rUS/strings.xml
new file mode 100644
index 0000000..6df96d8
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-es-rUS/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Para realizar llamadas y enviar mensajes con Wi-Fi, primero solicítale al proveedor que instale el servicio. Luego, vuelve a activar Llamada con Wi-Fi en Configuración."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Regístrate con tu proveedor"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Llamada con Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-es/strings.xml b/core/res/res/values-mcc310-mnc220-es/strings.xml
new file mode 100644
index 0000000..5292afe
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-es/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Para hacer llamadas y enviar mensajes por Wi-Fi, solicita a tu operador que configure este servicio y, cuando lo haga, vuelve a activar las llamadas por Wi-Fi en Ajustes."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Regístrate con tu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Llamada por Wi-Fi de %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-fr/strings.xml b/core/res/res/values-mcc310-mnc220-fr/strings.xml
new file mode 100644
index 0000000..dec321e
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-fr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Pour passer des appels et envoyer des messages via le Wi-Fi, demandez d\'abord à votre opérateur de configurer ce service. Ensuite, réactivez les appels Wi-Fi dans les paramètres."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Inscrivez-vous auprès de votre opérateur."</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Appels Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-it/strings.xml b/core/res/res/values-mcc310-mnc220-it/strings.xml
new file mode 100644
index 0000000..04bfca3
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-it/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Per poter effettuare chiamate e inviare messaggi tramite Wi-Fi, devi chiedere all\'operatore di attivare il servizio. Dopodiché, riattiva le chiamate Wi-Fi dalle Impostazioni."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registrati con il tuo operatore"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Chiamate Wi-Fi %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-ja/strings.xml b/core/res/res/values-mcc310-mnc220-ja/strings.xml
new file mode 100644
index 0000000..71956ec
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-ja/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Wi-Fi 経由で音声通話の発信やメッセージの送信を行うには、携帯通信会社に Wi-Fi サービスを申し込んだ上で、設定画面で Wi-Fi 発信を再度 ON にしてください。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"携帯通信会社に登録してください"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Wi-Fi 通話(%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-ko/strings.xml b/core/res/res/values-mcc310-mnc220-ko/strings.xml
new file mode 100644
index 0000000..40d0865
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-ko/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Wi-Fi를 사용하여 전화를 걸고 메시지를 보내려면 먼저 이동통신사에 문의하여 이 기능을 설정해야 합니다. 그런 다음 설정에서 Wi-Fi 통화를 사용 설정하시기 바랍니다."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"이동통신사에 등록"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Wi-Fi 통화"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-nb/strings.xml b/core/res/res/values-mcc310-mnc220-nb/strings.xml
new file mode 100644
index 0000000..798bb7d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-nb/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Du må be operatøren din om å konfigurere denne tjenesten før du kan ringe og sende meldinger via Wi-Fi. Deretter slår du på Wi-Fi-anrop igjen fra innstillingene."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registrer deg hos operatøren din"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Wi-Fi-anrop"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-nl/strings.xml b/core/res/res/values-mcc310-mnc220-nl/strings.xml
new file mode 100644
index 0000000..ed37e20
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-nl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Als je wilt bellen en berichten wilt verzenden via wifi, moet je eerst je provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via Instellingen."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registreren bij je provider"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Bellen via wifi van %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-pl/strings.xml b/core/res/res/values-mcc310-mnc220-pl/strings.xml
new file mode 100644
index 0000000..077c426
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-pl/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Aby dzwonić i wysyłać wiadomości przez Wi-Fi, poproś swojego operatora o skonfigurowanie tej usługi. Potem ponownie włącz połączenia przez Wi-Fi w Ustawieniach."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Zarejestruj u operatora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Połączenia przez Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-pt-rPT/strings.xml b/core/res/res/values-mcc310-mnc220-pt-rPT/strings.xml
new file mode 100644
index 0000000..20799a7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-pt-rPT/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Para fazer chamadas e enviar mensagens por Wi-Fi, comece por pedir ao seu operador para configurar o serviço. Em seguida, nas Definições, ative novamente as Chamadas Wi-Fi."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registar-se junto do seu operador"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Chamadas Wi-Fi da %s"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-pt/strings.xml b/core/res/res/values-mcc310-mnc220-pt/strings.xml
new file mode 100644
index 0000000..3a32281
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-pt/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente as chamadas por Wi-Fi nas configurações."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Faça registro na sua operadora"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s chamada Wi-Fi"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-ru/strings.xml b/core/res/res/values-mcc310-mnc220-ru/strings.xml
new file mode 100644
index 0000000..46a8d6f
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-ru/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Чтобы совершать звонки и отправлять сообщения по Wi-Fi, необходимо сначала обратиться к оператору связи и подключить эту услугу. После этого вы сможете снова выбрать этот параметр в настройках."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Укажите оператора и зарегистрируйтесь"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"Звонки по Wi-Fi (%s)"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-sv/strings.xml b/core/res/res/values-mcc310-mnc220-sv/strings.xml
new file mode 100644
index 0000000..056824d
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-sv/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Om du vill ringa samtal och skicka meddelanden via Wi-Fi ber du först operatören att konfigurera tjänsten. Därefter kan du aktivera Wi-Fi-samtal på nytt från Inställningar."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Registrera dig hos operatören"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Wi-Fi-samtal"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-tr/strings.xml b/core/res/res/values-mcc310-mnc220-tr/strings.xml
new file mode 100644
index 0000000..47aa7b7
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-tr/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"Kablosuz ağ üzerinden telefon etmek ve ileti göndermek için ilk önce operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın."</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"Operatörünüze kaydolun"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Kablosuz Çağrı"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc220-zh-rCN/strings.xml
new file mode 100644
index 0000000..a64a3b5
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-zh-rCN/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"要通过 WLAN 打电话和发信息,请先让您的运营商开通此服务,然后再到“设置”中重新开启 WLAN 通话功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"向您的运营商注册"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s WLAN 通话功能"</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220-zh-rTW/strings.xml b/core/res/res/values-mcc310-mnc220-zh-rTW/strings.xml
new file mode 100644
index 0000000..1673efc
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220-zh-rTW/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 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 my 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.
+*/
+ -->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item msgid="6238990105876016549">"如要透過 Wi-Fi 撥打電話及傳送訊息,請先要求您的行動通訊業者開通這項服務,然後再到「設定」啟用 Wi-Fi 通話功能。"</item>
+ </string-array>
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item msgid="2866631708941520085">"向您的行動通訊業者註冊"</item>
+ </string-array>
+ <string name="wfcSpnFormat" msgid="3422704506272221128">"%s Wi-Fi 通話"</string>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index b9d8661..be8577a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4430,7 +4430,9 @@
inside of its frozen icicle in addition to meta-data such as
the current cursor position. By default this is disabled;
it can be useful when the contents of a text view is not stored
- in a persistent place such as a content provider. -->
+ in a persistent place such as a content provider. For
+ {@link android.widget.EditText} it is always enabled, regardless
+ of the value of the attribute. -->
<attr name="freezesText" format="boolean" />
<!-- If set, causes words that are longer than the view is wide
to be ellipsized instead of broken in the middle.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3a5336c..1496d09 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -429,8 +429,10 @@
sets. -->
<attr name="multiArch" format ="boolean" />
- <!-- Specify abiOverride for multiArch application. -->
- <attr name="abiOverride" />
+ <!-- Specify whether the 32 bit version of the ABI should be used in a
+ multiArch application. If both abioverride flag (i.e. using abi option of abd install)
+ and use32bitAbi are used, then use32bit is ignored.-->
+ <attr name="use32bitAbi" />
<!-- Specify whether a component is allowed to have multiple instances
of itself running in different processes. Use with the activity
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5c5aff0..69d005c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2696,7 +2696,7 @@
<public type="attr" name="endX" />
<public type="attr" name="endY" />
<public type="attr" name="offset" />
- <public type="attr" name="abiOverride" />
+ <public type="attr" name="use32bitAbi" />
<public type="attr" name="bitmap" />
<public type="attr" name="hotSpotX" />
<public type="attr" name="hotSpotY" />
diff --git a/core/tests/coretests/res/layout/animator_set_squares.xml b/core/tests/coretests/res/layout/animator_set_squares.xml
index 23e6eea..6888248 100644
--- a/core/tests/coretests/res/layout/animator_set_squares.xml
+++ b/core/tests/coretests/res/layout/animator_set_squares.xml
@@ -4,7 +4,8 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:id="@+id/container">
<View
android:layout_width="50dp"
android:layout_height="50dp"
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
new file mode 100644
index 0000000..dc60423
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.transition.Transition.TransitionListener;
+import android.transition.Transition.TransitionListenerAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.support.test.espresso.Espresso.onView;
+
+public class FadeTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+ Activity mActivity;
+ public FadeTransitionTest() {
+ super(AnimatorSetActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mActivity = getActivity();
+ }
+
+ @SmallTest
+ public void testFadeOutAndIn() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ TransitionLatch latch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ assertFalse(square1.getTransitionAlpha() == 0 || square1.getTransitionAlpha() == 1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ latch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ final float transitionAlpha = square1.getTransitionAlpha();
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " + transitionAlpha,
+ transitionAlpha > 0 && transitionAlpha < 1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ }
+
+ @SmallTest
+ public void testFadeOutInterrupt() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+ fadeOut.addListener(fadeOutValueCheck);
+ TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ Thread.sleep(100);
+
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+ fadeIn.addListener(fadeInValueCheck);
+ TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+ assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+ fadeOutValueCheck.pauseTransitionAlpha,
+ fadeOutValueCheck.pauseTransitionAlpha > 0 &&
+ fadeOutValueCheck.pauseTransitionAlpha < 1);
+
+ assertTrue(inLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ }
+
+ @SmallTest
+ public void testFadeInInterrupt() throws Throwable {
+ final View square1 = mActivity.findViewById(R.id.square1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ square1.setVisibility(View.INVISIBLE);
+ }
+ });
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+ fadeIn.addListener(fadeInValueCheck);
+ TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ Thread.sleep(100);
+
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+ fadeOut.addListener(fadeOutValueCheck);
+ TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+ assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+ fadeInValueCheck.pauseTransitionAlpha,
+ fadeInValueCheck.pauseTransitionAlpha > 0 &&
+ fadeInValueCheck.pauseTransitionAlpha < 1);
+
+ assertTrue(outLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+ }
+
+ public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+ final int visibility) throws Throwable {
+ final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+ final View view = sceneRoot.findViewById(viewId);
+ TransitionLatch latch = new TransitionLatch();
+ transition.addListener(latch);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ view.setVisibility(visibility);
+ }
+ });
+ return latch;
+ }
+
+ public static class TransitionLatch implements TransitionListener {
+ public CountDownLatch startLatch = new CountDownLatch(1);
+ public CountDownLatch endLatch = new CountDownLatch(1);
+ public CountDownLatch cancelLatch = new CountDownLatch(1);
+ public CountDownLatch pauseLatch = new CountDownLatch(1);
+ public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ endLatch.countDown();
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ cancelLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ resumeLatch.countDown();
+ }
+ }
+
+ private static class FadeValueCheck extends TransitionListenerAdapter {
+ public float startTransitionAlpha;
+ public float pauseTransitionAlpha;
+ private final View mView;
+
+ public FadeValueCheck(View view) {
+ mView = view;
+ }
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startTransitionAlpha = mView.getTransitionAlpha();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseTransitionAlpha = mView.getTransitionAlpha();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
new file mode 100644
index 0000000..04c8b8c
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 android.widget;
+
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ViewGroup;
+
+public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
+
+ private EditText mEditText;
+ private final String RTL_STRING = "مرحبا الروبوت مرحبا الروبوت مرحبا الروبوت";
+
+ public EditorCursorTest() {
+ super(TextViewActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mEditText = new EditText(getActivity());
+ mEditText.setTextSize(30);
+ mEditText.setSingleLine(true);
+ mEditText.setLines(1);
+ mEditText.setPadding(15, 15, 15, 15);
+ ViewGroup.LayoutParams editTextLayoutParams = new ViewGroup.LayoutParams(200,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ mEditText.setLayoutParams(editTextLayoutParams);
+
+ final FrameLayout layout = new FrameLayout(getActivity());
+ ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ layout.setLayoutParams(layoutParams);
+ layout.addView(mEditText);
+
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ getActivity().setContentView(layout);
+ mEditText.requestFocus();
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ }
+
+ @SmallTest
+ public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception {
+ // Asserts that when an EditText has LTR text, and cursor is at the end (right),
+ // cursor is drawn to the right edge of the view
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
+ int length = mEditText.getText().length();
+ mEditText.setSelection(length, length);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ Editor editor = mEditText.getEditorForTesting();
+ Drawable drawable = editor.getCursorDrawable()[0];
+ Rect drawableBounds = drawable.getBounds();
+ Rect drawablePadding = new Rect();
+ drawable.getPadding(drawablePadding);
+
+ // right edge of the view including the scroll
+ int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
+ - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX();
+ int diff = drawableBounds.right - drawablePadding.right - maxRight;
+ assertTrue(diff >= 0 && diff <= 1);
+ }
+
+ @SmallTest
+ public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception {
+ // Asserts that when an EditText has LTR text, and cursor is at the beginning,
+ // cursor is drawn to the left edge of the view
+
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa");
+ mEditText.setSelection(0, 0);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
+ Rect drawableBounds = drawable.getBounds();
+ Rect drawablePadding = new Rect();
+ drawable.getPadding(drawablePadding);
+
+ int diff = drawableBounds.left + drawablePadding.left;
+ assertTrue(diff >= 0 && diff <= 1);
+ }
+
+ @SmallTest
+ public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception {
+ // Asserts that when an EditText has RTL text, and cursor is at the end,
+ // cursor is drawn to the left edge of the view
+
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mEditText.setText(RTL_STRING);
+ mEditText.setSelection(0, 0);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
+ Rect drawableBounds = drawable.getBounds();
+ Rect drawablePadding = new Rect();
+ drawable.getPadding(drawablePadding);
+
+ int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight()
+ - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX();
+
+ int diff = drawableBounds.right - drawablePadding.right - maxRight;
+ assertTrue(diff >= 0 && diff <= 1);
+ }
+
+ @SmallTest
+ public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception {
+ // Asserts that when an EditText has RTL text, and cursor is at the beginning,
+ // cursor is drawn to the right edge of the view
+
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mEditText.setText(RTL_STRING);
+ int length = mEditText.getText().length();
+ mEditText.setSelection(length, length);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0];
+ Rect drawableBounds = drawable.getBounds();
+ Rect drawablePadding = new Rect();
+ drawable.getPadding(drawablePadding);
+
+ int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left;
+ assertTrue(diff >= 0 && diff <= 1);
+ }
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index d133a12..93581db 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -20,11 +20,13 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
import android.os.Parcel;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.LocaleList;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import android.view.inputmethod.InputMethodSubtype;
@@ -230,7 +232,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_EN_US))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(autoSubtype, result.get(0));
}
@@ -251,8 +256,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
- assertEquals(1, result.size());
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_EN_US))
+ .getResources(),
+ imi);
verifyEquality(nonAutoEnUS, result.get(0));
}
@@ -271,7 +278,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_GB, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_EN_GB))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoEnGB, result.get(0));
}
@@ -292,7 +302,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_FR))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
}
@@ -309,7 +322,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR_CA, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_FR_CA))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
}
@@ -327,7 +343,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_JA_JP, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_JA_JP))
+ .getResources(),
+ imi);
assertEquals(3, result.size());
verifyEquality(nonAutoJa, result.get(0));
verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1));
@@ -344,7 +363,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FIL_PH, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_FIL_PH))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoFil, result.get(0));
}
@@ -361,7 +383,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FI, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_FI))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoJa, result.get(0));
}
@@ -376,7 +401,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_IN))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoIn, result.get(0));
}
@@ -389,7 +417,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_ID))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoIn, result.get(0));
}
@@ -402,7 +433,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_IN))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoId, result.get(0));
}
@@ -415,7 +449,10 @@
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
subtypes);
final ArrayList<InputMethodSubtype> result =
- callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ createTargetContextWithLocales(new LocaleList(LOCALE_ID))
+ .getResources(),
+ imi);
assertEquals(1, result.size());
verifyEquality(nonAutoId, result.get(0));
}
@@ -568,24 +605,11 @@
}
}
- private ArrayList<InputMethodSubtype> callGetImplicitlyApplicableSubtypesLockedWithLocale(
- final Locale locale, final InputMethodInfo imi) {
- final Context context = getInstrumentation().getTargetContext();
- final Locale initialLocale = context.getResources().getConfiguration().locale;
- try {
- context.getResources().getConfiguration().setLocale(locale);
- return InputMethodUtils.getImplicitlyApplicableSubtypesLocked(context.getResources(),
- imi);
- } finally {
- context.getResources().getConfiguration().setLocale(initialLocale);
- }
- }
-
private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) {
- final Context context = getInstrumentation().getTargetContext();
- final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesWithLocale(
- context, isSystemReady, preinstalledImes, systemLocale));
+ final Context context = createTargetContextWithLocales(new LocaleList(systemLocale));
+ final String[] actualImeNames = getPackageNames(
+ InputMethodUtils.getDefaultEnabledImes(context, isSystemReady, preinstalledImes));
assertEquals(expectedImeNames.length, actualImeNames.length);
for (int i = 0; i < expectedImeNames.length; ++i) {
assertEquals(expectedImeNames[i], actualImeNames[i]);
@@ -606,16 +630,12 @@
}
}
- private static ArrayList<InputMethodInfo> callGetDefaultEnabledImesWithLocale(
- final Context context, final boolean isSystemReady,
- final ArrayList<InputMethodInfo> imis, final Locale locale) {
- final Locale initialLocale = context.getResources().getConfiguration().locale;
- try {
- context.getResources().getConfiguration().setLocale(locale);
- return InputMethodUtils.getDefaultEnabledImes(context, isSystemReady, imis);
- } finally {
- context.getResources().getConfiguration().setLocale(initialLocale);
- }
+ private Context createTargetContextWithLocales(final LocaleList locales) {
+ final Configuration resourceConfiguration = new Configuration();
+ resourceConfiguration.setLocales(locales);
+ return getInstrumentation()
+ .getTargetContext()
+ .createConfigurationContext(resourceConfiguration);
}
private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 7b43947..1f242a3 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -317,11 +317,14 @@
tests/microbench/DisplayListCanvasBench.cpp \
tests/microbench/LinearAllocatorBench.cpp \
tests/microbench/PathParserBench.cpp \
- tests/microbench/ShadowBench.cpp
+ tests/microbench/ShadowBench.cpp \
+ tests/microbench/TaskManagerBench.cpp
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
tests/microbench/FrameBuilderBench.cpp
endif
+LOCAL_CLANG := true # workaround gcc bug
+
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index e3a5f3e..f83e1fa 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -201,8 +201,7 @@
renderer.caches().dropShadowCache.setFontRenderer(fontRenderer);
ShadowTexture* texture = renderer.caches().dropShadowCache.get(
- op.paint, (const char*) op.glyphs,
- op.glyphCount, textShadow.radius, op.positions);
+ op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!texture) return;
@@ -277,8 +276,7 @@
bool forceFinish = (renderType == TextRenderType::Flush);
bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr;
- fontRenderer.renderPosText(op.paint, localOpClip,
- (const char*) op.glyphs, op.glyphCount, x, y,
+ fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y,
op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish);
if (mustDirtyRenderTarget) {
@@ -701,8 +699,7 @@
bool mustDirtyRenderTarget = renderer.offscreenRenderTarget();
const Rect localSpaceClip = state.computedState.computeLocalSpaceClip();
- if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip,
- reinterpret_cast<const char*>(op.glyphs), op.glyphCount,
+ if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount,
op.path, op.hOffset, op.vOffset,
mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) {
if (mustDirtyRenderTarget) {
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 3db14b5..00560d7 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -428,7 +428,7 @@
if (!glyphs || count <= 0) return;
int bytesCount = 2 * count;
- DrawOp* op = new (alloc()) DrawTextOnPathOp(refText((const char*) glyphs, bytesCount),
+ DrawOp* op = new (alloc()) DrawTextOnPathOp(refBuffer<glyph_t>(glyphs, count),
bytesCount, count, refPath(&path),
hOffset, vOffset, refPaint(&paint));
addDrawOp(op);
@@ -442,11 +442,10 @@
if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
int bytesCount = count * 2;
- const char* text = refText((const char*) glyphs, bytesCount);
positions = refBuffer<float>(positions, count * 2);
Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ DrawOp* op = new (alloc()) DrawTextOp(refBuffer<glyph_t>(glyphs, count), bytesCount, count,
x, y, positions, refPaint(&paint), totalAdvance, bounds);
addDrawOp(op);
drawTextDecorations(x, y, totalAdvance, paint);
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 06e72a0..e5711e3 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -256,10 +256,6 @@
return dstBuffer;
}
- inline char* refText(const char* text, size_t byteLength) {
- return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
- }
-
inline const SkPath* refPath(const SkPath* path) {
if (!path) return nullptr;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 92217edc..20501ba 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1229,7 +1229,7 @@
class DrawSomeTextOp : public DrawOp {
public:
- DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint)
+ DrawSomeTextOp(const glyph_t* text, int bytesCount, int count, const SkPaint* paint)
: DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
virtual void output(int level, uint32_t logFlags) const override {
@@ -1251,14 +1251,14 @@
}
protected:
- const char* mText;
+ const glyph_t* mText;
int mBytesCount;
int mCount;
};
class DrawTextOnPathOp : public DrawSomeTextOp {
public:
- DrawTextOnPathOp(const char* text, int bytesCount, int count,
+ DrawTextOnPathOp(const glyph_t* text, int bytesCount, int count,
const SkPath* path, float hOffset, float vOffset, const SkPaint* paint)
: DrawSomeTextOp(text, bytesCount, count, paint),
mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
@@ -1280,7 +1280,7 @@
class DrawTextOp : public DrawStrokableOp {
public:
- DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
+ DrawTextOp(const glyph_t* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds)
: DrawStrokableOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
@@ -1341,7 +1341,7 @@
virtual const char* name() override { return "DrawText"; }
private:
- const char* mText;
+ const glyph_t* mText;
int mBytesCount;
int mCount;
float mX;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 68bae6d..1b618c6 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -557,7 +557,7 @@
mCurrentFont = Font::create(this, paint, matrix);
}
-FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
+FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs,
int numGlyphs, float radius, const float* positions) {
checkInit();
@@ -577,7 +577,7 @@
mBounds = nullptr;
Rect bounds;
- mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions);
+ mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions);
uint32_t intRadius = Blur::convertRadiusToInt(radius);
uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
@@ -609,7 +609,7 @@
// text has non-whitespace, so draw and blur to create the shadow
// NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted
// TODO: don't draw pure whitespace in the first place, and avoid needing this check
- mCurrentFont->render(paint, text, numGlyphs, penX, penY,
+ mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY,
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
// Unbind any PBO we might have used
@@ -643,17 +643,17 @@
issueDrawCommand();
}
-void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs,
+void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
const SkMatrix& matrix) {
Font* font = Font::create(this, paint, matrix);
- font->precache(paint, text, numGlyphs);
+ font->precache(paint, glyphs, numGlyphs);
}
void FontRenderer::endPrecaching() {
checkTextureUpdate();
}
-bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions,
Rect* bounds, TextDrawFunctor* functor, bool forceFinish) {
if (!mCurrentFont) {
@@ -662,7 +662,7 @@
}
initRender(clip, bounds, functor);
- mCurrentFont->render(paint, text, numGlyphs, x, y, positions);
+ mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions);
if (forceFinish) {
finishRender();
@@ -671,7 +671,7 @@
return mDrawn;
}
-bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, const SkPath* path, float hOffset, float vOffset,
Rect* bounds, TextDrawFunctor* functor) {
if (!mCurrentFont) {
@@ -680,7 +680,7 @@
}
initRender(clip, bounds, functor);
- mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset);
+ mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset);
finishRender();
return mDrawn;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9994498..e10a81b 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -104,14 +104,14 @@
void setFont(const SkPaint* paint, const SkMatrix& matrix);
- void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix);
+ void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkMatrix& matrix);
void endPrecaching();
- bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
+ bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions,
Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true);
- bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
+ bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs,
int numGlyphs, const SkPath* path,
float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor);
@@ -125,7 +125,7 @@
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
- DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs,
+ DropShadow renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, int numGlyphs,
float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
diff --git a/libs/hwui/FrameStatsObserver.h b/libs/hwui/FrameMetricsObserver.h
similarity index 86%
rename from libs/hwui/FrameStatsObserver.h
rename to libs/hwui/FrameMetricsObserver.h
index 7abc9f1..2b42a80 100644
--- a/libs/hwui/FrameStatsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -23,9 +23,9 @@
namespace android {
namespace uirenderer {
-class FrameStatsObserver : public VirtualLightRefBase {
+class FrameMetricsObserver : public VirtualLightRefBase {
public:
- virtual void notify(BufferPool::Buffer* buffer);
+ virtual void notify(BufferPool::Buffer* buffer, int dropCount);
};
}; // namespace uirenderer
diff --git a/libs/hwui/FrameStatsReporter.h b/libs/hwui/FrameMetricsReporter.h
similarity index 83%
rename from libs/hwui/FrameStatsReporter.h
rename to libs/hwui/FrameMetricsReporter.h
index b8a9432..0831d24 100644
--- a/libs/hwui/FrameStatsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -21,7 +21,7 @@
#include "BufferPool.h"
#include "FrameInfo.h"
-#include "FrameStatsObserver.h"
+#include "FrameMetricsObserver.h"
#include <string.h>
#include <vector>
@@ -29,18 +29,18 @@
namespace android {
namespace uirenderer {
-class FrameStatsReporter {
+class FrameMetricsReporter {
public:
- FrameStatsReporter() {
+ FrameMetricsReporter() {
mBufferPool = new BufferPool(kBufferSize, kBufferCount);
LOG_ALWAYS_FATAL_IF(mBufferPool.get() == nullptr, "OOM: unable to allocate buffer pool");
}
- void addObserver(FrameStatsObserver* observer) {
+ void addObserver(FrameMetricsObserver* observer) {
mObservers.push_back(observer);
}
- bool removeObserver(FrameStatsObserver* observer) {
+ bool removeObserver(FrameMetricsObserver* observer) {
for (size_t i = 0; i < mObservers.size(); i++) {
if (mObservers[i].get() == observer) {
mObservers.erase(mObservers.begin() + i);
@@ -54,7 +54,7 @@
return mObservers.size() > 0;
}
- void reportFrameStats(const int64_t* stats) {
+ void reportFrameMetrics(const int64_t* stats) {
BufferPool::Buffer* statsBuffer = mBufferPool->acquire();
if (statsBuffer != nullptr) {
@@ -63,11 +63,12 @@
// notify on requested threads
for (size_t i = 0; i < mObservers.size(); i++) {
- mObservers[i]->notify(statsBuffer);
+ mObservers[i]->notify(statsBuffer, mDroppedReports);
}
// drop our reference
statsBuffer->release();
+ mDroppedReports = 0;
} else {
mDroppedReports++;
}
@@ -79,7 +80,7 @@
static const size_t kBufferCount = 3;
static const size_t kBufferSize = static_cast<size_t>(FrameInfoIndex::NumIndexes);
- std::vector< sp<FrameStatsObserver> > mObservers;
+ std::vector< sp<FrameMetricsObserver> > mObservers;
sp<BufferPool> mBufferPool;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 587be92..b7a5923 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1949,7 +1949,7 @@
}
}
-void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
+void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const glyph_t* glyphs,
int count, const float* positions,
FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.textureState().activateTexture(0);
@@ -1963,7 +1963,7 @@
// if shader-based correction is enabled
mCaches.dropShadowCache.setFontRenderer(fontRenderer);
ShadowTexture* texture = mCaches.dropShadowCache.get(
- paint, text, count, textShadow.radius, positions);
+ paint, glyphs, count, textShadow.radius, positions);
// If the drop shadow exceeds the max texture size or couldn't be
// allocated, skip drawing
if (!texture) return;
@@ -2084,14 +2084,14 @@
mState.setProjectionPathMask(allocator, path);
}
-void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+void OpenGLRenderer::drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
if (drawOpMode == DrawOpMode::kImmediate) {
// The checks for corner-case ignorable text and quick rejection is only done for immediate
// drawing as ops from DeferredDisplayList are already filtered for these
- if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
+ if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) ||
quickRejectSetupScissor(bounds)) {
return;
}
@@ -2115,7 +2115,7 @@
if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
- drawTextShadow(paint, text, count, positions, fontRenderer,
+ drawTextShadow(paint, glyphs, count, positions, fontRenderer,
alpha, oldX, oldY);
}
@@ -2156,10 +2156,10 @@
if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) {
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
- status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y,
+ status = fontRenderer.renderPosText(&paintCopy, clip, glyphs, count, x, y,
positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
} else {
- status = fontRenderer.renderPosText(paint, clip, text, count, x, y,
+ status = fontRenderer.renderPosText(paint, clip, glyphs, count, x, y,
positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish);
}
@@ -2173,9 +2173,9 @@
mDirty = true;
}
-void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
+void OpenGLRenderer::drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count,
const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) {
- if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
+ if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) {
return;
}
@@ -2198,7 +2198,7 @@
const Rect* clip = &writableSnapshot()->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
- if (fontRenderer.renderTextOnPath(paint, clip, text, count, path,
+ if (fontRenderer.renderTextOnPath(paint, clip, glyphs, count, path,
hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) {
dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
mDirty = true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 84bc9b0..dacd8cc 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -191,9 +191,9 @@
void drawPath(const SkPath* path, const SkPaint* paint);
void drawLines(const float* points, int count, const SkPaint* paint);
void drawPoints(const float* points, int count, const SkPaint* paint);
- void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path,
+ void drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path,
float hOffset, float vOffset, const SkPaint* paint);
- void drawText(const char* text, int bytesCount, int count, float x, float y,
+ void drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = DrawOpMode::kImmediate);
void drawRects(const float* rects, int count, const SkPaint* paint);
@@ -647,7 +647,7 @@
* @param x The x coordinate where the shadow will be drawn
* @param y The y coordinate where the shadow will be drawn
*/
- void drawTextShadow(const SkPaint* paint, const char* text, int count,
+ void drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count,
const float* positions, FontRenderer& fontRenderer, int alpha,
float x, float y);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 3e11151..249b5b0 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -31,9 +31,6 @@
// Compile-time properties
///////////////////////////////////////////////////////////////////////////////
-// If turned on, text is interpreted as glyphs instead of UTF-16
-#define RENDER_TEXT_AS_GLYPHS 1
-
// Textures used by layers must have dimensions multiples of this number
#define LAYER_SIZE 64
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index fe4b3d75..e1f0b2a 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -37,9 +37,9 @@
hash = JenkinsHashMix(hash, flags);
hash = JenkinsHashMix(hash, android::hash_type(italicStyle));
hash = JenkinsHashMix(hash, android::hash_type(scaleX));
- if (text) {
+ if (glyphs) {
hash = JenkinsHashMixShorts(
- hash, reinterpret_cast<const uint16_t*>(text), glyphCount);
+ hash, reinterpret_cast<const uint16_t*>(glyphs), glyphCount);
}
if (positions) {
for (uint32_t i = 0; i < glyphCount * 2; i++) {
@@ -71,11 +71,11 @@
if (lhs.scaleX < rhs.scaleX) return -1;
if (lhs.scaleX > rhs.scaleX) return +1;
- if (lhs.text != rhs.text) {
- if (!lhs.text) return -1;
- if (!rhs.text) return +1;
+ if (lhs.glyphs != rhs.glyphs) {
+ if (!lhs.glyphs) return -1;
+ if (!rhs.glyphs) return +1;
- deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t));
+ deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t));
if (deltaInt != 0) return deltaInt;
}
@@ -145,7 +145,7 @@
mCache.clear();
}
-ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs,
+ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
float radius, const float* positions) {
ShadowText entry(paint, radius, numGlyphs, glyphs, positions);
ShadowTexture* texture = mCache.get(entry);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index cf64788..d536c40 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -35,26 +35,21 @@
struct ShadowText {
ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr),
- flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) {
+ flags(0), italicStyle(0.0f), scaleX(0), glyphs(nullptr), positions(nullptr) {
}
// len is the number of bytes in text
- ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText,
- const float* positions):
- glyphCount(glyphCount), radius(radius), positions(positions) {
- // TODO: Propagate this through the API, we should not cast here
- text = (const char16_t*) srcText;
-
- textSize = paint->getTextSize();
- typeface = paint->getTypeface();
-
- flags = 0;
- if (paint->isFakeBoldText()) {
- flags |= Font::kFakeBold;
- }
-
- italicStyle = paint->getTextSkewX();
- scaleX = paint->getTextScaleX();
+ ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const glyph_t* srcGlyphs,
+ const float* positions)
+ : glyphCount(glyphCount)
+ , radius(radius)
+ , textSize(paint->getTextSize())
+ , typeface(paint->getTypeface())
+ , flags(paint->isFakeBoldText() ? Font::kFakeBold : 0)
+ , italicStyle(paint->getTextSkewX())
+ , scaleX(paint->getTextScaleX())
+ , glyphs(srcGlyphs)
+ , positions(positions) {
}
~ShadowText() {
@@ -73,8 +68,8 @@
}
void copyTextLocally() {
- str.setTo((const char16_t*) text, glyphCount);
- text = str.string();
+ str.setTo(reinterpret_cast<const char16_t*>(glyphs), glyphCount);
+ glyphs = reinterpret_cast<const glyph_t*>(str.string());
if (positions != nullptr) {
positionsCopy.clear();
positionsCopy.appendArray(positions, glyphCount * 2);
@@ -89,7 +84,7 @@
uint32_t flags;
float italicStyle;
float scaleX;
- const char16_t* text;
+ const glyph_t* glyphs;
const float* positions;
// Not directly used to compute the cache key
@@ -135,7 +130,7 @@
*/
void operator()(ShadowText& text, ShadowTexture*& texture) override;
- ShadowTexture* get(const SkPaint* paint, const char* text,
+ ShadowTexture* get(const SkPaint* paint, const glyph_t* text,
int numGlyphs, float radius, const float* positions);
/**
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index dc82041..9a825fd 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -291,15 +291,15 @@
return cachedGlyph;
}
-void Font::render(const SkPaint* paint, const char *text,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr,
+ render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr,
0, 0, nullptr, positions);
}
-void Font::render(const SkPaint* paint, const char *text, int numGlyphs,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs,
const SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == nullptr) {
+ if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
@@ -315,7 +315,7 @@
float pathLength = SkScalarToFloat(measure.getLength());
if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2));
+ float textWidth = SkScalarToFloat(paint->measureText(glyphs, numGlyphs * 2));
float pathOffset = pathLength;
if (paint->getTextAlign() == SkPaint::kCenter_Align) {
textWidth *= 0.5f;
@@ -325,7 +325,7 @@
}
while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
+ glyph_t glyph = *(glyphs++);
if (IS_END_OF_STRING(glyph)) {
break;
@@ -345,26 +345,26 @@
}
}
-void Font::measure(const SkPaint* paint, const char* text,
+void Font::measure(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, Rect *bounds, const float* positions) {
if (bounds == nullptr) {
ALOGE("No return rectangle provided to measure text");
return;
}
bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
+ render(paint, glyphs, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions);
}
-void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
+void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) {
ATRACE_NAME("Precache Glyphs");
- if (numGlyphs == 0 || text == nullptr) {
+ if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
int glyphsCount = 0;
while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
+ glyph_t glyph = *(glyphs++);
// Reached the end of the string
if (IS_END_OF_STRING(glyph)) {
@@ -376,10 +376,10 @@
}
}
-void Font::render(const SkPaint* paint, const char* text,
+void Font::render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == nullptr) {
+ if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
@@ -396,7 +396,7 @@
int glyphsCount = 0;
while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
+ glyph_t glyph = *(glyphs++);
// Reached the end of the string
if (IS_END_OF_STRING(glyph)) {
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 59518a1..e8882d9 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -82,10 +82,10 @@
~Font();
- void render(const SkPaint* paint, const char* text,
+ void render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, const float* positions);
- void render(const SkPaint* paint, const char* text,
+ void render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, const SkPath* path, float hOffset, float vOffset);
const Font::FontDescription& getDescription() const {
@@ -111,13 +111,13 @@
MEASURE,
};
- void precache(const SkPaint* paint, const char* text, int numGlyphs);
+ void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs);
- void render(const SkPaint* paint, const char *text,
+ void render(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
- void measure(const SkPaint* paint, const char* text,
+ void measure(const SkPaint* paint, const glyph_t* glyphs,
int numGlyphs, Rect *bounds, const float* positions);
void invalidateTextureCache(CacheTexture* cacheTexture = nullptr);
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index 4e5debe..aa77d98 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -40,26 +40,9 @@
#define CACHE_BLOCK_ROUNDING_SIZE 4
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-
- static inline glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(cache, glyph) cache->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
+typedef uint16_t glyph_t;
+#define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph)
+#define IS_END_OF_STRING(glyph) false
#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ea702c0..4f528b1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -507,8 +507,8 @@
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
- if (CC_UNLIKELY(mFrameStatsReporter.get() != nullptr)) {
- mFrameStatsReporter->reportFrameStats(mCurrentFrameInfo->data());
+ if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
+ mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data());
}
GpuMemoryTracker::onFrameCompleted();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 168166e..1f81970 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -20,7 +20,7 @@
#include "DamageAccumulator.h"
#include "FrameInfo.h"
#include "FrameInfoVisualizer.h"
-#include "FrameStatsReporter.h"
+#include "FrameMetricsReporter.h"
#include "IContextFactory.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
@@ -142,26 +142,26 @@
return mRenderThread.renderState();
}
- void addFrameStatsObserver(FrameStatsObserver* observer) {
- if (mFrameStatsReporter.get() == nullptr) {
- mFrameStatsReporter.reset(new FrameStatsReporter());
+ void addFrameMetricsObserver(FrameMetricsObserver* observer) {
+ if (mFrameMetricsReporter.get() == nullptr) {
+ mFrameMetricsReporter.reset(new FrameMetricsReporter());
}
- mFrameStatsReporter->addObserver(observer);
+ mFrameMetricsReporter->addObserver(observer);
}
- void removeFrameStatsObserver(FrameStatsObserver* observer) {
- if (mFrameStatsReporter.get() != nullptr) {
- mFrameStatsReporter->removeObserver(observer);
- if (!mFrameStatsReporter->hasObservers()) {
- mFrameStatsReporter.reset(nullptr);
+ void removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+ if (mFrameMetricsReporter.get() != nullptr) {
+ mFrameMetricsReporter->removeObserver(observer);
+ if (!mFrameMetricsReporter->hasObservers()) {
+ mFrameMetricsReporter.reset(nullptr);
}
}
}
long getDroppedFrameReportCount() {
- if (mFrameStatsReporter.get() != nullptr) {
- return mFrameStatsReporter->getDroppedReports();
+ if (mFrameMetricsReporter.get() != nullptr) {
+ return mFrameMetricsReporter->getDroppedReports();
}
return 0;
@@ -215,7 +215,7 @@
std::string mName;
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
- std::unique_ptr<FrameStatsReporter> mFrameStatsReporter;
+ std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter;
std::set<RenderNode*> mPrefetechedLayers;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 7c6cd7e..04223a7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -568,17 +568,17 @@
post(task);
}
-CREATE_BRIDGE2(addFrameStatsObserver, CanvasContext* context,
- FrameStatsObserver* frameStatsObserver) {
- args->context->addFrameStatsObserver(args->frameStatsObserver);
+CREATE_BRIDGE2(addFrameMetricsObserver, CanvasContext* context,
+ FrameMetricsObserver* frameStatsObserver) {
+ args->context->addFrameMetricsObserver(args->frameStatsObserver);
if (args->frameStatsObserver != nullptr) {
args->frameStatsObserver->decStrong(args->context);
}
return nullptr;
}
-void RenderProxy::addFrameStatsObserver(FrameStatsObserver* observer) {
- SETUP_TASK(addFrameStatsObserver);
+void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observer) {
+ SETUP_TASK(addFrameMetricsObserver);
args->context = mContext;
args->frameStatsObserver = observer;
if (observer != nullptr) {
@@ -587,17 +587,17 @@
post(task);
}
-CREATE_BRIDGE2(removeFrameStatsObserver, CanvasContext* context,
- FrameStatsObserver* frameStatsObserver) {
- args->context->removeFrameStatsObserver(args->frameStatsObserver);
+CREATE_BRIDGE2(removeFrameMetricsObserver, CanvasContext* context,
+ FrameMetricsObserver* frameStatsObserver) {
+ args->context->removeFrameMetricsObserver(args->frameStatsObserver);
if (args->frameStatsObserver != nullptr) {
args->frameStatsObserver->decStrong(args->context);
}
return nullptr;
}
-void RenderProxy::removeFrameStatsObserver(FrameStatsObserver* observer) {
- SETUP_TASK(removeFrameStatsObserver);
+void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+ SETUP_TASK(removeFrameMetricsObserver);
args->context = mContext;
args->frameStatsObserver = observer;
if (observer != nullptr) {
@@ -606,16 +606,6 @@
post(task);
}
-CREATE_BRIDGE1(getDroppedFrameReportCount, CanvasContext* context) {
- return (void*) args->context->getDroppedFrameReportCount();
-}
-
-long RenderProxy::getDroppedFrameReportCount() {
- SETUP_TASK(getDroppedFrameReportCount);
- args->context = mContext;
- return (long) postAndWait(task);
-}
-
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 178724a..8d65a82 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,7 +29,7 @@
#include <utils/StrongPointer.h>
#include "../Caches.h"
-#include "../FrameStatsObserver.h"
+#include "../FrameMetricsObserver.h"
#include "../IContextFactory.h"
#include "CanvasContext.h"
#include "DrawFrameTask.h"
@@ -113,8 +113,8 @@
ANDROID_API void drawRenderNode(RenderNode* node);
ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
- ANDROID_API void addFrameStatsObserver(FrameStatsObserver* observer);
- ANDROID_API void removeFrameStatsObserver(FrameStatsObserver* observer);
+ ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
+ ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API long getDroppedFrameReportCount();
private:
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 5ed7aa4..3440d03 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -58,27 +58,22 @@
return layerUpdater;
}
-void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
- const SkPaint& paint, float x, float y) {
- // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
- LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
- "must use glyph encoding");
+void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text,
+ std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
+ float* outTotalAdvance, Rect* outBounds) {
+ Rect bounds;
+ float totalAdvance = 0;
SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
- float totalAdvance = 0;
- std::vector<glyph_t> glyphs;
- std::vector<float> positions;
- Rect bounds;
while (*text != '\0') {
SkUnichar unichar = SkUTF8_NextUnichar(&text);
glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar);
autoCache.getCache()->unicharToGlyph(unichar);
// push glyph and its relative position
- glyphs.push_back(glyph);
- positions.push_back(totalAdvance);
- positions.push_back(0);
+ outGlyphs->push_back(glyph);
+ outPositions->push_back(totalAdvance);
+ outPositions->push_back(0);
// compute bounds
SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar);
@@ -91,6 +86,23 @@
paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL);
totalAdvance += skWidth;
}
+ *outBounds = bounds;
+ *outTotalAdvance = totalAdvance;
+}
+
+void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text,
+ const SkPaint& paint, float x, float y) {
+ // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
+ LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
+ "must use glyph encoding");
+ SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
+ SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
+
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ float totalAdvance;
+ Rect bounds;
+ layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
// apply alignment via x parameter (which JNI layer would have done)
if (paint.getTextAlign() == SkPaint::kCenter_Align) {
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ae08142..6f23705 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -205,6 +205,10 @@
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
+ static void layoutTextUnscaled(const SkPaint& paint, const char* text,
+ std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
+ float* outTotalAdvance, Rect* outBounds);
+
static void drawTextToCanvas(TestCanvas* canvas, const char* text,
const SkPaint& paint, float x, float y);
diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp
new file mode 100644
index 0000000..0ea30e47
--- /dev/null
+++ b/libs/hwui/tests/microbench/TaskManagerBench.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "thread/Task.h"
+#include "thread/TaskManager.h"
+#include "thread/TaskProcessor.h"
+#include "tests/microbench/MicroBench.h"
+
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+
+class TrivialTask : public Task<char> {};
+
+class TrivialProcessor : public TaskProcessor<char> {
+public:
+ TrivialProcessor(TaskManager* manager)
+ : TaskProcessor(manager) {}
+ virtual ~TrivialProcessor() {}
+ virtual void onProcess(const sp<Task<char> >& task) override {
+ TrivialTask* t = static_cast<TrivialTask*>(task.get());
+ t->setResult(reinterpret_cast<intptr_t>(t) % 16 == 0 ? 'a' : 'b');
+ }
+};
+
+BENCHMARK_NO_ARG(BM_TaskManager_allocateTask);
+void BM_TaskManager_allocateTask::Run(int iters) {
+ std::vector<sp<TrivialTask> > tasks;
+ tasks.reserve(iters);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ tasks.emplace_back(new TrivialTask);
+ MicroBench::DoNotOptimize(tasks.back());
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_TaskManager_enqueueTask);
+void BM_TaskManager_enqueueTask::Run(int iters) {
+ TaskManager taskManager;
+ sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
+ std::vector<sp<TrivialTask> > tasks;
+ tasks.reserve(iters);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ tasks.emplace_back(new TrivialTask);
+ MicroBench::DoNotOptimize(tasks.back());
+ processor->add(tasks.back());
+ }
+ StopBenchmarkTiming();
+
+ for (sp<TrivialTask>& task : tasks) {
+ task->getResult();
+ }
+}
+
+BENCHMARK_NO_ARG(BM_TaskManager_enqueueRunDeleteTask);
+void BM_TaskManager_enqueueRunDeleteTask::Run(int iters) {
+ TaskManager taskManager;
+ sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
+ std::vector<sp<TrivialTask> > tasks;
+ tasks.reserve(iters);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ tasks.emplace_back(new TrivialTask);
+ MicroBench::DoNotOptimize(tasks.back());
+ processor->add(tasks.back());
+ }
+ for (sp<TrivialTask>& task : tasks) {
+ MicroBench::DoNotOptimize(task->getResult());
+ }
+ tasks.clear();
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index c54f2c3..0d26df2 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -21,29 +21,31 @@
#include "utils/Blur.h"
#include "tests/common/TestUtils.h"
-#include <SkBlurDrawLooper.h>
#include <SkPaint.h>
using namespace android;
using namespace android::uirenderer;
RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+ SkPaint paint;
+ paint.setTextSize(20);
+
GammaFontRenderer gammaFontRenderer;
FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
- TextDropShadowCache cache(5000);
+ fontRenderer.setFont(&paint, SkMatrix::I());
+ TextDropShadowCache cache(MB(5));
cache.setFontRenderer(fontRenderer);
- SkPaint paint;
- paint.setLooper(SkBlurDrawLooper::Create((SkColor)0xFFFFFFFF,
- Blur::convertRadiusToSigma(10), 10, 10))->unref();
- std::string msg("This is a test");
- std::unique_ptr<float[]> positions(new float[msg.length()]);
- for (size_t i = 0; i < msg.length(); i++) {
- positions[i] = i * 10.0f;
- }
- fontRenderer.setFont(&paint, SkMatrix::I());
- ShadowTexture* texture = cache.get(&paint, msg.c_str(), msg.length(),
- 10.0f, positions.get());
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ float totalAdvance;
+ uirenderer::Rect bounds;
+ TestUtils::layoutTextUnscaled(paint, "This is a test",
+ &glyphs, &positions, &totalAdvance, &bounds);
+ EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized";
+
+ ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data());
+
ASSERT_TRUE(texture);
ASSERT_FALSE(texture->cleanup);
ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize());
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index a07845e..d346b85 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -103,7 +103,7 @@
return true;
}
-bool TaskManager::WorkerThread::addTask(TaskWrapper task) {
+bool TaskManager::WorkerThread::addTask(const TaskWrapper& task) {
if (!isRunning()) {
run(mName.string(), PRIORITY_DEFAULT);
} else if (exitPending()) {
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index d0eb304..e4808f7 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -80,7 +80,7 @@
public:
WorkerThread(const String8 name): mSignal(Condition::WAKE_UP_ONE), mName(name) { }
- bool addTask(TaskWrapper task);
+ bool addTask(const TaskWrapper& task);
size_t getTaskCount() const;
void exit();
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index c194711..a3bbdfc 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -242,6 +243,7 @@
private int mFlags = 0x0;
private HashSet<String> mTags;
private String mFormattedTags;
+ private Bundle mBundle; // lazy-initialized, may be null
private AudioAttributes() {
}
@@ -295,6 +297,20 @@
/**
* @hide
+ * Return the Bundle of data.
+ * @return a copy of the Bundle for this instance, may be null.
+ */
+ @SystemApi
+ public Bundle getBundle() {
+ if (mBundle == null) {
+ return mBundle;
+ } else {
+ return new Bundle(mBundle);
+ }
+ }
+
+ /**
+ * @hide
* Return the set of tags.
* @return a read-only set of all tags stored as strings.
*/
@@ -327,6 +343,7 @@
private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
private int mFlags = 0x0;
private HashSet<String> mTags = new HashSet<String>();
+ private Bundle mBundle;
/**
* Constructs a new Builder with the defaults.
@@ -365,6 +382,9 @@
aa.mFlags = mFlags;
aa.mTags = (HashSet<String>) mTags.clone();
aa.mFormattedTags = TextUtils.join(";", mTags);
+ if (mBundle != null) {
+ aa.mBundle = new Bundle(mBundle);
+ }
return aa;
}
@@ -453,6 +473,25 @@
/**
* @hide
+ * Adds a Bundle of data
+ * @param bundle a non-null Bundle
+ * @return the same builder instance
+ */
+ @SystemApi
+ public Builder addBundle(@NonNull Bundle bundle) {
+ if (bundle == null) {
+ throw new IllegalArgumentException("Illegal null bundle");
+ }
+ if (mBundle == null) {
+ mBundle = new Bundle(bundle);
+ } else {
+ mBundle.putAll(bundle);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
* Add a custom tag stored as a string
* @param tag
* @return the same Builder instance.
@@ -584,6 +623,10 @@
* see definition of kAudioAttributesMarshallTagFlattenTags
*/
public final static int FLATTEN_TAGS = 0x1;
+
+ private final static int ATTR_PARCEL_IS_NULL_BUNDLE = -1977;
+ private final static int ATTR_PARCEL_IS_VALID_BUNDLE = 1980;
+
/**
* When adding tags for writeToParcel(Parcel, int), add them in the list of flags (| NEW_FLAG)
*/
@@ -602,6 +645,12 @@
} else if ((flags & FLATTEN_TAGS) == FLATTEN_TAGS) {
dest.writeString(mFormattedTags);
}
+ if (mBundle == null) {
+ dest.writeInt(ATTR_PARCEL_IS_NULL_BUNDLE);
+ } else {
+ dest.writeInt(ATTR_PARCEL_IS_VALID_BUNDLE);
+ dest.writeBundle(mBundle);
+ }
}
private AudioAttributes(Parcel in) {
@@ -621,6 +670,16 @@
}
mFormattedTags = TextUtils.join(";", mTags);
}
+ switch (in.readInt()) {
+ case ATTR_PARCEL_IS_NULL_BUNDLE:
+ mBundle = null;
+ break;
+ case ATTR_PARCEL_IS_VALID_BUNDLE:
+ mBundle = new Bundle(in.readBundle());
+ break;
+ default:
+ Log.e(TAG, "Illegal value unmarshalling AudioAttributes, can't initialize bundle");
+ }
}
public static final Parcelable.Creator<AudioAttributes> CREATOR
@@ -655,7 +714,7 @@
@Override
public int hashCode() {
- return Objects.hash(mContentType, mFlags, mSource, mUsage, mFormattedTags);
+ return Objects.hash(mContentType, mFlags, mSource, mUsage, mFormattedTags, mBundle);
}
@Override
@@ -664,7 +723,8 @@
+ " usage=" + mUsage
+ " content=" + mContentType
+ " flags=0x" + Integer.toHexString(mFlags).toUpperCase()
- + " tags=" + mFormattedTags);
+ + " tags=" + mFormattedTags
+ + " bundle=" + (mBundle == null ? "null" : mBundle.toString()));
}
/** @hide */
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 800b914..e342385 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1014,9 +1014,12 @@
* Reads audio data from the audio hardware for recording into a byte array.
* The format specified in the AudioRecord constructor should be
* {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+ * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
* @param audioData the array to which the recorded audio data is written.
- * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
+ * @param offsetInBytes index in audioData to which the data is written expressed in bytes.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInBytes the number of requested bytes.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
* <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
* is read.
@@ -1025,7 +1028,8 @@
* @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
- * The number of bytes will not exceed sizeInBytes.
+ * The number of bytes will be a multiple of the frame size in bytes
+ * not to exceed sizeInBytes.
*/
public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
@ReadMode int readMode) {
@@ -1053,12 +1057,14 @@
* The format specified in the AudioRecord constructor should be
* {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
* @param audioData the array to which the recorded audio data is written.
- * @param offsetInShorts index in audioData from which the data is written expressed in shorts.
+ * @param offsetInShorts index in audioData to which the data is written expressed in shorts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInShorts the number of requested shorts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
- * The number of shorts will not exceed sizeInShorts.
+ * The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
*/
public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
@@ -1070,7 +1076,9 @@
* {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
* @param audioData the array to which the recorded audio data is written.
* @param offsetInShorts index in audioData from which the data is written expressed in shorts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInShorts the number of requested shorts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
* <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
* is read.
@@ -1079,7 +1087,7 @@
* @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
- * The number of shorts will not exceed sizeInShorts.
+ * The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
*/
public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
@ReadMode int readMode) {
@@ -1108,7 +1116,9 @@
* {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
* @param audioData the array to which the recorded audio data is written.
* @param offsetInFloats index in audioData from which the data is written.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInFloats the number of requested floats.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
* <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
* is read.
@@ -1117,7 +1127,7 @@
* @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
- * The number of floats will not exceed sizeInFloats.
+ * The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
*/
public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
@ReadMode int readMode) {
@@ -1154,6 +1164,7 @@
* The representation of the data in the buffer will depend on the format specified in
* the AudioRecord constructor, and will be native endian.
* @param audioBuffer the direct buffer to which the recorded audio data is written.
+ * Data is written to audioBuffer.position().
* @param sizeInBytes the number of requested bytes. It is recommended but not enforced
* that the number of bytes requested be a multiple of the frame size (sample size in
* bytes multiplied by the channel count).
@@ -1161,7 +1172,7 @@
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
* The number of bytes will not exceed sizeInBytes.
- * The number of bytes read will truncated to be a multiple of the frame size.
+ * The number of bytes read will be truncated to be a multiple of the frame size.
*/
public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
return read(audioBuffer, sizeInBytes, READ_BLOCKING);
@@ -1175,6 +1186,7 @@
* The representation of the data in the buffer will depend on the format specified in
* the AudioRecord constructor, and will be native endian.
* @param audioBuffer the direct buffer to which the recorded audio data is written.
+ * Data is written to audioBuffer.position().
* @param sizeInBytes the number of requested bytes. It is recommended but not enforced
* that the number of bytes requested be a multiple of the frame size (sample size in
* bytes multiplied by the channel count).
@@ -1187,7 +1199,7 @@
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
* The number of bytes will not exceed sizeInBytes.
- * The number of bytes read will truncated to be a multiple of the frame size.
+ * The number of bytes read will be truncated to be a multiple of the frame size.
*/
public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
if (mState != STATE_INITIALIZED) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b26b310..bdf6d9f 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1774,6 +1774,7 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+ * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
* <p>
* In streaming mode, the write will normally block until all the data has been enqueued for
* playback, and will return a full transfer count. However, if the track is stopped or paused
@@ -1786,7 +1787,9 @@
* @param audioData the array that holds the data to play.
* @param offsetInBytes the offset expressed in bytes in audioData where the data to write
* starts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInBytes the number of bytes to write in audioData after the offset.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @return zero or the positive number of bytes that were written, or
* {@link #ERROR_INVALID_OPERATION}
* if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1795,6 +1798,8 @@
* needs to be recreated.
* The dead object error code is not returned if some data was successfully transferred.
* In this case, the error is returned at the next write().
+ * The number of bytes will be a multiple of the frame size in bytes
+ * not to exceed sizeInBytes.
*
* This is equivalent to {@link #write(byte[], int, int, int)} with <code>writeMode</code>
* set to {@link #WRITE_BLOCKING}.
@@ -1808,6 +1813,7 @@
* or copies audio data for later playback (static buffer mode).
* The format specified in the AudioTrack constructor should be
* {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+ * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
* <p>
* In streaming mode, the blocking behavior depends on the write mode. If the write mode is
* {@link #WRITE_BLOCKING}, the write will normally block until all the data has been enqueued
@@ -1823,7 +1829,9 @@
* @param audioData the array that holds the data to play.
* @param offsetInBytes the offset expressed in bytes in audioData where the data to write
* starts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInBytes the number of bytes to write in audioData after the offset.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
* effect in static mode.
* <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1838,6 +1846,8 @@
* needs to be recreated.
* The dead object error code is not returned if some data was successfully transferred.
* In this case, the error is returned at the next write().
+ * The number of bytes will be a multiple of the frame size in bytes
+ * not to exceed sizeInBytes.
*/
public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
@WriteMode int writeMode) {
@@ -1887,7 +1897,9 @@
* @param audioData the array that holds the data to play.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to play
* starts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInShorts the number of shorts to read in audioData after the offset.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @return zero or the positive number of shorts that were written, or
* {@link #ERROR_INVALID_OPERATION}
* if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1896,6 +1908,7 @@
* needs to be recreated.
* The dead object error code is not returned if some data was successfully transferred.
* In this case, the error is returned at the next write().
+ * The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
*
* This is equivalent to {@link #write(short[], int, int, int)} with <code>writeMode</code>
* set to {@link #WRITE_BLOCKING}.
@@ -1923,7 +1936,9 @@
* @param audioData the array that holds the data to write.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to write
* starts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInShorts the number of shorts to read in audioData after the offset.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
* effect in static mode.
* <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1938,6 +1953,7 @@
* needs to be recreated.
* The dead object error code is not returned if some data was successfully transferred.
* In this case, the error is returned at the next write().
+ * The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
*/
public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
@WriteMode int writeMode) {
@@ -1999,7 +2015,9 @@
* to provide samples values within the nominal range.
* @param offsetInFloats the offset, expressed as a number of floats,
* in audioData where the data to write starts.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param sizeInFloats the number of floats to write in audioData after the offset.
+ * Must not be negative, or cause the data access to go out of bounds of the array.
* @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
* effect in static mode.
* <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -2014,6 +2032,7 @@
* needs to be recreated.
* The dead object error code is not returned if some data was successfully transferred.
* In this case, the error is returned at the next write().
+ * The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
*/
public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
@WriteMode int writeMode) {
@@ -2075,7 +2094,9 @@
* <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
* have been advanced to reflect the amount of data that was successfully written to
* the AudioTrack.
- * @param sizeInBytes number of bytes to write.
+ * @param sizeInBytes number of bytes to write. It is recommended but not enforced
+ * that the number of bytes requested be a multiple of the frame size (sample size in
+ * bytes multiplied by the channel count).
* <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
* @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
* effect in static mode.
@@ -2142,7 +2163,9 @@
* <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
* have been advanced to reflect the amount of data that was successfully written to
* the AudioTrack.
- * @param sizeInBytes number of bytes to write.
+ * @param sizeInBytes number of bytes to write. It is recommended but not enforced
+ * that the number of bytes requested be a multiple of the frame size (sample size in
+ * bytes multiplied by the channel count).
* <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
* @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}.
* <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index d189333..12a220f 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -88,7 +88,7 @@
// For the recording session
void connect(in IBinder sessionToken, in Uri channelUri, in Bundle params, int userId);
- void startRecording(in IBinder sessionToken, int userId);
+ void startRecording(in IBinder sessionToken, in Uri programHint, int userId);
void stopRecording(in IBinder sessionToken, int userId);
// For TV input hardware binding
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 408a762..07781bc 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -58,6 +58,6 @@
// For the recording session
void connect(in Uri channelUri, in Bundle params);
void disconnect();
- void startRecording();
+ void startRecording(in Uri programHint);
void stopRecording();
}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 4ac5876..b15acef 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.media.PlaybackParams;
@@ -220,7 +221,7 @@
break;
}
case DO_START_RECORDING: {
- mTvInputRecordingSessionImpl.startRecording();
+ mTvInputRecordingSessionImpl.startRecording((Uri) msg.obj);
break;
}
case DO_STOP_RECORDING: {
@@ -366,8 +367,8 @@
}
@Override
- public void startRecording() {
- mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_RECORDING));
+ public void startRecording(@Nullable Uri programHint) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_START_RECORDING, programHint));
}
@Override
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1cd1958..d76408e 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2039,22 +2039,25 @@
}
/**
- * Starts TV program recording for the current recording session.
+ * Starts TV program recording in the current recording session.
+ *
+ * @param programHint The URI for the TV program to record as a hint, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
*/
- void startRecording() {
+ void startRecording(@Nullable Uri programHint) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
return;
}
try {
- mService.startRecording(mToken, mUserId);
+ mService.startRecording(mToken, programHint, mUserId);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
/**
- * Stops TV program recording for the current recording session.
+ * Stops TV program recording in the current recording session.
*/
void stopRecording() {
if (mToken == null) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index d48b2c8..91f1ee9 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1699,16 +1699,26 @@
* Called when the application requests to start recording. Recording must start
* immediately.
*
+ * <p>The application may supply the URI for a TV program as a hint for filling in program
+ * specific data fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
+ * A non-null {@code programHint} implies the started recording should be of that specific
+ * program, whereas null {@code programHint} does not impose such a requirement and the
+ * recording can span across multiple TV programs. In either case, the application must call
+ * {@link TvRecordingClient#stopRecording()} to stop the recording.
+ *
* <p>The session must call either {@link #notifyRecordingStarted()} or
- * {@link #notifyError(int)}}.
+ * {@link #notifyError(int)}.
+ *
+ * @param programHint The URI for the TV program to record as a hint, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be {@code null}.
*/
- public abstract void onStartRecording();
+ public abstract void onStartRecording(@Nullable Uri programHint);
/**
* Called when the application requests to stop recording. Recording must stop immediately.
*
* <p>The session must call either {@link #notifyRecordingStopped(Uri)} or
- * {@link #notifyError(int)}}.
+ * {@link #notifyError(int)}.
*/
public abstract void onStopRecording();
@@ -1744,11 +1754,11 @@
}
/**
- * Calls {@link #onStartRecording()}.
+ * Calls {@link #onStartRecording(Uri)}.
*
*/
- void startRecording() {
- onStartRecording();
+ void startRecording(@Nullable Uri programHint) {
+ onStartRecording(programHint);
}
/**
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 1d80068..b943c3d 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -17,6 +17,7 @@
package android.media.tv;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
import android.net.Uri;
@@ -131,20 +132,31 @@
}
/**
- * Starts TV program recording for the current recording session. It is expected that recording
+ * Starts TV program recording in the current recording session. It is expected that recording
* starts immediately after calling this method.
*
+ * <p>The application may supply the URI for a TV program as a hint to the corresponding TV
+ * input service for filling in program specific data fields in the
+ * {@link android.media.tv.TvContract.RecordedPrograms} table. A non-null {@code programHint}
+ * implies the started recording should be of that specific program, whereas null
+ * {@code programHint} does not impose such a requirement and the recording can span across
+ * multiple TV programs. In either case, the caller must call {@link #stopRecording()} to stop
+ * the recording.
+ *
* <p>The recording session will respond by calling
* {@link RecordingCallback#onRecordingStarted()} or {@link RecordingCallback#onError(int)}.
+ *
+ * @param programHint The URI for the TV program to record as a hint, built by
+ * {@link TvContract#buildProgramUri(long)}. Can be null.
*/
- public void startRecording() {
+ public void startRecording(@Nullable Uri programHint) {
if (mSession != null) {
- mSession.startRecording();
+ mSession.startRecording(programHint);
}
}
/**
- * Stops TV program recording for the current recording session. It is expected that recording
+ * Stops TV program recording in the current recording session. It is expected that recording
* stops immediately after calling this method.
*
* <p>The recording session will respond by calling
@@ -325,7 +337,7 @@
@Override
public void onRecordingStopped(TvInputManager.Session session, Uri recordedProgramUri) {
if (DEBUG) {
- Log.d(TAG, "onRecordingStopped()");
+ Log.d(TAG, "onRecordingStopped(recordedProgramUri= " + recordedProgramUri + ")");
}
if (this != mSessionCallback) {
Log.w(TAG, "onRecordingStopped - session not created");
@@ -337,7 +349,7 @@
@Override
public void onError(TvInputManager.Session session, int error) {
if (DEBUG) {
- Log.d(TAG, "onError()");
+ Log.d(TAG, "onError(error=" + error + ")");
}
if (this != mSessionCallback) {
Log.w(TAG, "onError - session not created");
@@ -350,7 +362,8 @@
public void onSessionEvent(TvInputManager.Session session, String eventType,
Bundle eventArgs) {
if (DEBUG) {
- Log.d(TAG, "onSessionEvent(" + eventType + ")");
+ Log.d(TAG, "onSessionEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
+ + ")");
}
if (this != mSessionCallback) {
Log.w(TAG, "onSessionEvent - session not created");
diff --git a/packages/DocumentsUI/res/layout/directory_cluster.xml b/packages/DocumentsUI/res/layout/directory_cluster.xml
index 2fa09d3..d84ef08 100644
--- a/packages/DocumentsUI/res/layout/directory_cluster.xml
+++ b/packages/DocumentsUI/res/layout/directory_cluster.xml
@@ -27,6 +27,7 @@
<FrameLayout
android:id="@+id/container_directory"
+ android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 223d729..d0364ff 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -17,8 +17,10 @@
<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:animateLayoutChanges="true">
+ android:background="@color/window_background"
+ android:outlineProvider="bounds"
+ android:elevation="4dp"
+ android:orientation="vertical">
<ProgressBar
android:id="@+id/progressbar"
@@ -43,6 +45,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+ android:background="@color/window_background"
android:visibility="gone">
<LinearLayout
@@ -81,7 +84,6 @@
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
- android:background="@color/window_background"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 70bee3c..d21b157 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -876,17 +876,8 @@
msgView.setText(msg);
imageView.setImageResource(drawable);
- content.animate().cancel(); // cancel any ongoing animations
-
- content.setAlpha(0);
mEmptyView.setVisibility(View.VISIBLE);
mRecView.setVisibility(View.GONE);
-
- // fade in the content, so it looks purdy like
- content.animate()
- .alpha(1f)
- .setDuration(EMPTY_REVEAL_DURATION)
- .setListener(null);
}
private void showDirectory() {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java
new file mode 100644
index 0000000..34f1120
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/ActivityTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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 com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StubProvider.ROOT_0_ID;
+import static com.android.documentsui.StubProvider.ROOT_1_ID;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.provider.DocumentsContract.Document;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.Until;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.MotionEvent;
+
+import com.android.documentsui.model.RootInfo;
+
+/**
+ * Provides basic test environment for UI tests:
+ * - Launches activity
+ * - Creates and gives access to test root directories and test files
+ * - Cleans up the test environment
+ */
+public abstract class ActivityTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
+
+ static final int TIMEOUT = 5000;
+
+ // Testing files. For custom ones, override initTestFiles().
+ public static final String dirName1 = "Dir1";
+ public static final String fileName1 = "file1.log";
+ public static final String fileName2 = "file12.png";
+ public static final String fileName3 = "anotherFile0.log";
+ public static final String fileName4 = "poodles.text";
+ public static final String fileNameNoRename = "NO_RENAMEfile.txt";
+
+ public UiBot bot;
+ public UiDevice device;
+ public Context context;
+
+ public RootInfo rootDir0;
+ public RootInfo rootDir1;
+
+ ContentResolver mResolver;
+ DocumentsProviderHelper mDocsHelper;
+ ContentProviderClient mClient;
+
+ public ActivityTest(Class<T> activityClass) {
+ super(activityClass);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ device = UiDevice.getInstance(getInstrumentation());
+ // NOTE: Must be the "target" context, else security checks in content provider will fail.
+ context = getInstrumentation().getTargetContext();
+ bot = new UiBot(device, context, TIMEOUT);
+
+ Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+ bot.revealLauncher();
+
+ mResolver = context.getContentResolver();
+ mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
+ mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
+
+ rootDir0 = mDocsHelper.getRoot(ROOT_0_ID);
+ rootDir1 = mDocsHelper.getRoot(ROOT_1_ID);
+
+ launchActivity();
+
+ bot.revealApp();
+ resetStorage();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mClient.release();
+ super.tearDown();
+ }
+
+ void launchActivity() {
+ final Intent intent = context.getPackageManager().getLaunchIntentForPackage(
+ UiBot.TARGET_PKG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ setActivityIntent(intent);
+ getActivity(); // Launch the activity.
+ }
+
+ void resetStorage() throws RemoteException {
+ mClient.call("clear", null, null);
+ device.waitForIdle();
+ }
+
+ void initTestFiles() throws RemoteException {
+ mDocsHelper.createFolder(rootDir0, dirName1);
+ mDocsHelper.createDocument(rootDir0, "text/plain", fileName1);
+ mDocsHelper.createDocument(rootDir0, "image/png", fileName2);
+ mDocsHelper.createDocumentWithFlags(rootDir0.documentId, "text/plain", fileNameNoRename,
+ Document.FLAG_SUPPORTS_WRITE);
+
+ mDocsHelper.createDocument(rootDir1, "text/plain", fileName3);
+ mDocsHelper.createDocument(rootDir1, "text/plain", fileName4);
+ }
+
+ void assertDefaultContentOfTestDir0() throws UiObjectNotFoundException {
+ bot.assertDocumentsCount(ROOT_0_ID, 4);
+ bot.assertHasDocuments(fileName1, fileName2, dirName1, fileNameNoRename);
+ }
+
+ void assertDefaultContentOfTestDir1() throws UiObjectNotFoundException {
+ bot.assertDocumentsCount(ROOT_1_ID, 2);
+ bot.assertHasDocuments(fileName3, fileName4);
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
index 5df4dca..af478ea 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
@@ -92,7 +92,8 @@
Uri uri = DocumentsContract.createDocument(mClient, parentUri, mimeType, name);
return uri;
} catch (RemoteException e) {
- throw new RuntimeException("Couldn't create document: " + name + " with mimetype " + mimeType, e);
+ throw new RuntimeException("Couldn't create document: " + name + " with mimetype "
+ + mimeType, e);
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
index 243c357..737b376 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
@@ -38,122 +38,74 @@
import com.android.documentsui.model.RootInfo;
@LargeTest
-public class DownloadsActivityUiTest extends ActivityInstrumentationTestCase2<DownloadsActivity> {
+public class DownloadsActivityUiTest extends ActivityTest<DownloadsActivity> {
private static final int TIMEOUT = 5000;
private static final String TAG = "DownloadsActivityUiTest";
private static final String TARGET_PKG = "com.android.documentsui";
private static final String LAUNCHER_PKG = "com.android.launcher";
- private UiBot mBot;
- private UiDevice mDevice;
- private Context mContext;
- private ContentResolver mResolver;
- private DocumentsProviderHelper mDocsHelper;
- private ContentProviderClient mClient;
- private RootInfo mRoot;
-
public DownloadsActivityUiTest() {
super(DownloadsActivity.class);
}
- public void setUp() throws Exception {
- // Initialize UiDevice instance.
- Instrumentation instrumentation = getInstrumentation();
-
- mDevice = UiDevice.getInstance(instrumentation);
-
- Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
-
- // Start from the home screen.
- mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
-
- // NOTE: Must be the "target" context, else security checks in content provider will fail.
- mContext = instrumentation.getTargetContext();
- mResolver = mContext.getContentResolver();
-
- mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
- mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
-
- mRoot = mDocsHelper.getRoot(ROOT_0_ID);
-
- // Open the Downloads activity on our stub provider root.
- Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_ROOT);
- intent.setDataAndType(mRoot.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
+ @Override
+ void launchActivity() {
+ final Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_ROOT);
+ intent.setDataAndType(rootDir0.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
setActivityIntent(intent);
- getActivity(); // Start the activity.
-
- // Wait for the app to appear.
- mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
- mDevice.waitForIdle();
-
- mBot = new UiBot(mDevice, mContext, TIMEOUT);
-
- resetStorage(); // Just in case a test failed and tearDown didn't happen.
+ getActivity(); // Launch the activity.
}
@Override
- protected void tearDown() throws Exception {
- Log.d(TAG, "Resetting storage from setUp");
- resetStorage();
- mClient.release();
-
- super.tearDown();
- }
-
- private void resetStorage() throws RemoteException {
- mClient.call("clear", null, null);
- // TODO: Would be nice to have an event to wait on here.
- mDevice.waitForIdle();
- }
-
- private void initTestFiles() throws RemoteException {
- mDocsHelper.createDocument(mRoot, "text/plain", "file0.log");
- mDocsHelper.createDocument(mRoot, "image/png", "file1.png");
- mDocsHelper.createDocument(mRoot, "text/csv", "file2.csv");
+ void initTestFiles() throws RemoteException {
+ mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log");
+ mDocsHelper.createDocument(rootDir0, "image/png", "file1.png");
+ mDocsHelper.createDocument(rootDir0, "text/csv", "file2.csv");
}
public void testWindowTitle() throws Exception {
initTestFiles();
- mBot.assertWindowTitle(ROOT_0_ID);
+ bot.assertWindowTitle(ROOT_0_ID);
}
public void testFilesListed() throws Exception {
initTestFiles();
- mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
+ bot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
}
public void testFilesList_LiveUpdate() throws Exception {
initTestFiles();
- mDocsHelper.createDocument(mRoot, "yummers/sandwich", "Ham & Cheese.sandwich");
- mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
+ mDocsHelper.createDocument(rootDir0, "yummers/sandwich", "Ham & Cheese.sandwich");
+
+ device.waitForIdle();
+ bot.assertHasDocuments("file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
}
public void testDeleteDocument() throws Exception {
initTestFiles();
- mBot.clickDocument("file1.png");
- mDevice.waitForIdle();
- mBot.menuDelete().click();
+ bot.clickDocument("file1.png");
+ device.waitForIdle();
+ bot.menuDelete().click();
- mBot.waitForDeleteSnackbar();
- assertFalse(mBot.hasDocuments("file1.png"));
+ bot.waitForDeleteSnackbar();
+ assertFalse(bot.hasDocuments("file1.png"));
- mBot.waitForDeleteSnackbarGone();
- assertFalse(mBot.hasDocuments("file1.png"));
+ bot.waitForDeleteSnackbarGone();
+ assertFalse(bot.hasDocuments("file1.png"));
}
public void testSupportsShare() throws Exception {
initTestFiles();
- mBot.clickDocument("file1.png");
- mDevice.waitForIdle();
- assertNotNull(mBot.menuShare());
+ bot.clickDocument("file1.png");
+ device.waitForIdle();
+ assertNotNull(bot.menuShare());
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 868dbbb..7fd2416 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -30,7 +30,6 @@
import android.support.test.uiautomator.Configurator;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
-import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.view.MotionEvent;
@@ -38,96 +37,35 @@
import com.android.documentsui.model.RootInfo;
@LargeTest
-public class FilesActivityUiTest extends ActivityInstrumentationTestCase2<FilesActivity> {
+public class FilesActivityUiTest extends ActivityTest<FilesActivity> {
private static final int TIMEOUT = 5000;
private static final String TAG = "FilesActivityUiTest";
private static final String TARGET_PKG = "com.android.documentsui";
private static final String LAUNCHER_PKG = "com.android.launcher";
- private UiBot mBot;
- private UiDevice mDevice;
- private Context mContext;
- private ContentResolver mResolver;
- private DocumentsProviderHelper mDocsHelper;
- private ContentProviderClient mClient;
- private RootInfo mRoot_0;
- private RootInfo mRoot_1;
-
public FilesActivityUiTest() {
super(FilesActivity.class);
}
- public void setUp() throws Exception {
- // Initialize UiDevice instance.
- Instrumentation instrumentation = getInstrumentation();
-
- mDevice = UiDevice.getInstance(instrumentation);
-
- Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
-
- // Start from the home screen.
- mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
-
- // NOTE: Must be the "target" context, else security checks in content provider will fail.
- mContext = instrumentation.getTargetContext();
- mResolver = mContext.getContentResolver();
-
- mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
- assertNotNull("Failed to acquire ContentProviderClient.", mClient);
- mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
-
- // Launch app.
- Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- setActivityIntent(intent);
- getActivity(); // Start the activity.
-
- // Wait for the app to appear.
- mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
- mDevice.waitForIdle();
-
- mBot = new UiBot(mDevice, mContext, TIMEOUT);
-
- resetStorage(); // Just incase a test failed and tearDown didn't happen.
- }
-
@Override
- protected void tearDown() throws Exception {
- Log.d(TAG, "Resetting storage from setUp");
- resetStorage();
- mClient.release();
+ public void initTestFiles() throws RemoteException {
+ mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log");
+ mDocsHelper.createDocument(rootDir0, "image/png", "file1.png");
+ mDocsHelper.createDocument(rootDir0, "text/csv", "file2.csv");
- super.tearDown();
- }
-
- private void resetStorage() throws RemoteException {
- mClient.call("clear", null, null);
- // TODO: Would be nice to have an event to wait on here.
- mDevice.waitForIdle();
- }
-
- private void initTestFiles() throws RemoteException {
- mRoot_0 = mDocsHelper.getRoot(ROOT_0_ID);
- mRoot_1 = mDocsHelper.getRoot(ROOT_1_ID);
-
- mDocsHelper.createDocument(mRoot_0, "text/plain", "file0.log");
- mDocsHelper.createDocument(mRoot_0, "image/png", "file1.png");
- mDocsHelper.createDocument(mRoot_0, "text/csv", "file2.csv");
-
- mDocsHelper.createDocument(mRoot_1, "text/plain", "anotherFile0.log");
- mDocsHelper.createDocument(mRoot_1, "text/plain", "poodles.text");
+ mDocsHelper.createDocument(rootDir1, "text/plain", "anotherFile0.log");
+ mDocsHelper.createDocument(rootDir1, "text/plain", "poodles.text");
}
public void testRootsListed() throws Exception {
initTestFiles();
- mBot.openRoot(ROOT_0_ID);
+ bot.openRoot(ROOT_0_ID);
// Should also have Drive, but that requires pre-configuration of devices
// We omit for now.
- mBot.assertHasRoots(
+ bot.assertHasRoots(
"Images",
"Videos",
"Audio",
@@ -140,60 +78,60 @@
public void testFilesListed() throws Exception {
initTestFiles();
- mBot.openRoot(ROOT_0_ID);
- mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
+ bot.openRoot(ROOT_0_ID);
+ bot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
}
public void testLoadsHomeByDefault() throws Exception {
initTestFiles();
- mDevice.waitForIdle();
- mBot.assertWindowTitle("Home");
+ device.waitForIdle();
+ bot.assertWindowTitle("Home");
}
public void testRootClickSetsWindowTitle() throws Exception {
initTestFiles();
- mBot.openRoot("Downloads");
- mBot.assertWindowTitle("Downloads");
+ bot.openRoot("Downloads");
+ bot.assertWindowTitle("Downloads");
}
public void testFilesList_LiveUpdate() throws Exception {
initTestFiles();
- mBot.openRoot(ROOT_0_ID);
- mDocsHelper.createDocument(mRoot_0, "yummers/sandwich", "Ham & Cheese.sandwich");
+ bot.openRoot(ROOT_0_ID);
+ mDocsHelper.createDocument(rootDir0, "yummers/sandwich", "Ham & Cheese.sandwich");
- mDevice.waitForIdle();
- mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
+ device.waitForIdle();
+ bot.assertHasDocuments("file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
}
public void testDeleteDocument() throws Exception {
initTestFiles();
- mBot.openRoot(ROOT_0_ID);
+ bot.openRoot(ROOT_0_ID);
- mBot.clickDocument("file1.png");
- mDevice.waitForIdle();
- mBot.menuDelete().click();
+ bot.clickDocument("file1.png");
+ device.waitForIdle();
+ bot.menuDelete().click();
- mBot.waitForDeleteSnackbar();
- assertFalse(mBot.hasDocuments("file1.png"));
+ bot.waitForDeleteSnackbar();
+ assertFalse(bot.hasDocuments("file1.png"));
- mBot.waitForDeleteSnackbarGone();
- assertFalse(mBot.hasDocuments("file1.png"));
+ bot.waitForDeleteSnackbarGone();
+ assertFalse(bot.hasDocuments("file1.png"));
// Now delete from another root.
- mBot.openRoot(ROOT_1_ID);
+ bot.openRoot(ROOT_1_ID);
- mBot.clickDocument("poodles.text");
- mDevice.waitForIdle();
- mBot.menuDelete().click();
+ bot.clickDocument("poodles.text");
+ device.waitForIdle();
+ bot.menuDelete().click();
- mBot.waitForDeleteSnackbar();
- assertFalse(mBot.hasDocuments("poodles.text"));
+ bot.waitForDeleteSnackbar();
+ assertFalse(bot.hasDocuments("poodles.text"));
- mBot.waitForDeleteSnackbarGone();
- assertFalse(mBot.hasDocuments("poodles.text"));
+ bot.waitForDeleteSnackbarGone();
+ assertFalse(bot.hasDocuments("poodles.text"));
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
index 5c6254f..303f2d1 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
@@ -17,7 +17,6 @@
package com.android.documentsui;
import static com.android.documentsui.StubProvider.ROOT_0_ID;
-import static com.android.documentsui.UiTestEnvironment.TIMEOUT;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -25,142 +24,141 @@
import android.test.suitebuilder.annotation.LargeTest;
@LargeTest
-public class RenameDocumentUiTest extends InstrumentationTestCase {
+public class RenameDocumentUiTest extends ActivityTest<FilesActivity> {
private static final String TAG = "RenamDocumentUiTest";
private final String newName = "kitties.log";
- private UiTestEnvironment mHelper;
+ public RenameDocumentUiTest() {
+ super(FilesActivity.class);
+ }
@Override
public void setUp() throws Exception {
super.setUp();
- mHelper = new UiTestEnvironment(getInstrumentation());
- mHelper.launch();
- mHelper.initTestFiles();
- mHelper.bot().openRoot(ROOT_0_ID);
+ initTestFiles();
+ bot.openRoot(ROOT_0_ID);
}
public void testRenameEnabled_SingleSelection() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().assertMenuEnabled(R.string.menu_rename, true);
+ bot.selectDocument(fileName1);
+ bot.openOverflowMenu();
+ bot.assertMenuEnabled(R.string.menu_rename, true);
// Dismiss more options window
- mHelper.device().pressBack();
+ device.pressBack();
}
public void testNoRenameSupport_SingleSelection() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileNameNoRename);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().assertMenuEnabled(R.string.menu_rename, false);
+ bot.selectDocument(fileNameNoRename);
+ bot.openOverflowMenu();
+ bot.assertMenuEnabled(R.string.menu_rename, false);
// Dismiss more options window
- mHelper.device().pressBack();
+ device.pressBack();
}
public void testOneHasRenameSupport_MultipleSelection() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().selectDocument(UiTestEnvironment.fileNameNoRename);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().assertMenuEnabled(R.string.menu_rename, false);
+ bot.selectDocument(fileName1);
+ bot.selectDocument(fileNameNoRename);
+ bot.openOverflowMenu();
+ bot.assertMenuEnabled(R.string.menu_rename, false);
// Dismiss more options window
- mHelper.device().pressBack();
+ device.pressBack();
}
public void testRenameDisabled_MultipleSelection() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().selectDocument(UiTestEnvironment.fileName2);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().assertMenuEnabled(R.string.menu_rename, false);
+ bot.selectDocument(fileName1);
+ bot.selectDocument(fileName2);
+ bot.openOverflowMenu();
+ bot.assertMenuEnabled(R.string.menu_rename, false);
// Dismiss more options window
- mHelper.device().pressBack();
+ device.pressBack();
}
public void testRenameFile_OkButton() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().openDialog(R.string.menu_rename);
- mHelper.bot().setDialogText(newName);
- mHelper.bot().dismissKeyboardIfPresent();
+ bot.selectDocument(fileName1);
+ bot.openOverflowMenu();
+ bot.openDialog(R.string.menu_rename);
+ bot.setDialogText(newName);
+ bot.dismissKeyboardIfPresent();
- mHelper.device().waitForIdle(TIMEOUT);
- mHelper.bot().findRenameDialogOkButton().click();
- mHelper.device().waitForIdle(TIMEOUT);
+ device.waitForIdle(TIMEOUT);
+ bot.findRenameDialogOkButton().click();
+ device.waitForIdle(TIMEOUT);
- mHelper.bot().assertDocument(UiTestEnvironment.fileName1, false);
- mHelper.bot().assertDocument(newName, true);
- mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+ bot.assertDocument(fileName1, false);
+ bot.assertDocument(newName, true);
+ bot.assertDocumentsCount(4);
}
public void testRenameFile_Enter() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().openDialog(R.string.menu_rename);
- mHelper.bot().setDialogText(newName);
+ bot.selectDocument(fileName1);
+ bot.openOverflowMenu();
+ bot.openDialog(R.string.menu_rename);
+ bot.setDialogText(newName);
pressEnter();
- mHelper.bot().assertDocument(UiTestEnvironment.fileName1, false);
- mHelper.bot().assertDocument(newName, true);
- mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+ bot.assertDocument(fileName1, false);
+ bot.assertDocument(newName, true);
+ bot.assertDocumentsCount(4);
}
public void testRenameFile_Cancel() throws Exception {
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().openDialog(R.string.menu_rename);
- mHelper.bot().setDialogText(newName);
- mHelper.bot().dismissKeyboardIfPresent();
+ bot.selectDocument(fileName1);
+ bot.openOverflowMenu();
+ bot.openDialog(R.string.menu_rename);
+ bot.setDialogText(newName);
+ bot.dismissKeyboardIfPresent();
- mHelper.device().waitForIdle(TIMEOUT);
- mHelper.bot().findRenameDialogCancelButton().click();
- mHelper.device().waitForIdle(TIMEOUT);
+ device.waitForIdle(TIMEOUT);
+ bot.findRenameDialogCancelButton().click();
+ device.waitForIdle(TIMEOUT);
- mHelper.bot().assertDocument(UiTestEnvironment.fileName1, true);
- mHelper.bot().assertDocument(newName, false);
- mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+ bot.assertDocument(fileName1, true);
+ bot.assertDocument(newName, false);
+ bot.assertDocumentsCount(4);
}
public void testRenameDir() throws Exception {
String oldName = "Dir1";
String newName = "Dir123";
- mHelper.bot().selectDocument(oldName);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().openDialog(R.string.menu_rename);
- mHelper.bot().setDialogText(newName);
+ bot.selectDocument(oldName);
+ bot.openOverflowMenu();
+ bot.openDialog(R.string.menu_rename);
+ bot.setDialogText(newName);
pressEnter();
- mHelper.bot().assertDocument(oldName, false);
- mHelper.bot().assertDocument(newName, true);
- mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+ bot.assertDocument(oldName, false);
+ bot.assertDocument(newName, true);
+ bot.assertDocumentsCount(4);
}
public void testRename_NameExists() throws Exception {
// Check that document with the new name exists
- mHelper.bot().assertDocument(UiTestEnvironment.fileName2, true);
- mHelper.bot().selectDocument(UiTestEnvironment.fileName1);
- mHelper.bot().openOverflowMenu();
- mHelper.bot().openDialog(R.string.menu_rename);
- mHelper.bot().setDialogText(UiTestEnvironment.fileName2);
+ bot.assertDocument(fileName2, true);
+ bot.selectDocument(fileName1);
+ bot.openOverflowMenu();
+ bot.openDialog(R.string.menu_rename);
+ bot.setDialogText(fileName2);
pressEnter();
- mHelper.bot().assertSnackbar(R.string.rename_error);
- mHelper.bot().assertDocument(UiTestEnvironment.fileName1, true);
- mHelper.bot().assertDocument(UiTestEnvironment.fileName2, true);
- mHelper.bot().assertDocumentsCount(mHelper.getDocumentsCountDir0());
+ bot.assertSnackbar(R.string.rename_error);
+ bot.assertDocument(fileName1, true);
+ bot.assertDocument(fileName2, true);
+ bot.assertDocumentsCount(4);
}
private void pressEnter() {
- mHelper.device().waitForIdle(TIMEOUT);
- mHelper.device().pressEnter();
- mHelper.device().waitForIdle(TIMEOUT);
+ device.waitForIdle(TIMEOUT);
+ device.pressEnter();
+ device.waitForIdle(TIMEOUT);
}
-
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
index 042ec85..61da3df 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
@@ -24,138 +24,132 @@
import android.test.suitebuilder.annotation.LargeTest;
@LargeTest
-public class SearchViewUiTest extends InstrumentationTestCase {
+public class SearchViewUiTest extends ActivityTest<FilesActivity> {
private static final String TAG = "SearchViewUiTest";
- private UiTestEnvironment mEnv;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mEnv = new UiTestEnvironment(getInstrumentation());
- mEnv.launch();
-
- }
-
- @Override
- protected void tearDown() throws Exception {
- mEnv.device().pressBack();
- super.tearDown();
+ public SearchViewUiTest() {
+ super(FilesActivity.class);
}
public void testSearchView_ExpandsOnClick() throws Exception {
- mEnv.bot().openSearchView();
- mEnv.bot().assertSearchTextFiledAndIcon(true, false);
+ bot.openSearchView();
+ bot.assertSearchTextFiledAndIcon(true, false);
}
public void testSearchView_CollapsesOnBack() throws Exception {
- mEnv.bot().openSearchView();
+ bot.openSearchView();
- mEnv.device().pressBack();
+ device.pressBack();
- mEnv.bot().assertSearchTextFiledAndIcon(false, true);
+ bot.assertSearchTextFiledAndIcon(false, true);
}
public void testSearchView_ClearsTextOnBack() throws Exception {
String query = "file2";
- mEnv.bot().openSearchView();
- mEnv.bot().setSearchQuery(query);
+ bot.openSearchView();
+ bot.setSearchQuery(query);
- mEnv.device().pressBack();
+ device.pressBack();
- mEnv.bot().assertSearchTextFiledAndIcon(false, true);
+ bot.assertSearchTextFiledAndIcon(false, true);
}
public void testSearch_ResultsFound() throws Exception {
- mEnv.initTestFiles();
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.assertDefaultContentOfTestDir0();
+ initTestFiles();
+ bot.openRoot(ROOT_0_ID);
+ assertDefaultContentOfTestDir0();
String query = "file1";
- mEnv.bot().openSearchView();
- mEnv.bot().setSearchQuery(query);
- mEnv.bot().assertSearchTextField(true, query);
+ bot.openSearchView();
+ bot.setSearchQuery(query);
+ bot.assertSearchTextField(true, query);
- mEnv.device().pressEnter();
+ device.pressEnter();
- mEnv.bot().assertDocumentsCountOnList(true, 2);
- mEnv.bot().assertHasDocuments(UiTestEnvironment.fileName1, UiTestEnvironment.fileName2);
- mEnv.bot().assertSearchTextField(false, query);
+ bot.assertDocumentsCountOnList(true, 2);
+ bot.assertHasDocuments(fileName1, fileName2);
+
+ bot.assertSearchTextField(false, query);
}
public void testSearchResultsFound_ClearsOnBack() throws Exception {
- mEnv.initTestFiles();
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.assertDefaultContentOfTestDir0();
+ initTestFiles();
+ bot.openRoot(ROOT_0_ID);
+ assertDefaultContentOfTestDir0();
- String query = UiTestEnvironment.fileName1;
- mEnv.bot().openSearchView();
- mEnv.bot().setSearchQuery(query);
+ String query = fileName1;
+ bot.openSearchView();
+ bot.setSearchQuery(query);
- mEnv.device().pressEnter();
- mEnv.device().pressBack();
- mEnv.assertDefaultContentOfTestDir0();
+ device.pressEnter();
+ device.pressBack();
+
+ assertDefaultContentOfTestDir0();
}
public void testSearch_NoResults() throws Exception {
- mEnv.initTestFiles();
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.assertDefaultContentOfTestDir0();
+ initTestFiles();
+ bot.openRoot(ROOT_0_ID);
+ assertDefaultContentOfTestDir0();
String query = "chocolate";
- mEnv.bot().openSearchView();
- mEnv.bot().setSearchQuery(query);
+ bot.openSearchView();
+ bot.setSearchQuery(query);
- mEnv.device().pressEnter();
+ device.pressEnter();
- mEnv.bot().assertDocumentsCountOnList(false, 0);
+ bot.assertDocumentsCountOnList(false, 0);
- String msg = String.valueOf(mEnv.context().getString(R.string.no_results));
- mEnv.bot().assertMessageTextView(String.format(msg, "TEST_ROOT_0"));
- mEnv.bot().assertSearchTextField(false, query);
+ device.waitForIdle();
+ String msg = String.valueOf(context.getString(R.string.no_results));
+ bot.assertMessageTextView(String.format(msg, "TEST_ROOT_0"));
+
+ bot.assertSearchTextField(false, query);
}
public void testSearchNoResults_ClearsOnBack() throws Exception {
- mEnv.initTestFiles();
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.assertDefaultContentOfTestDir0();
+ initTestFiles();
+ bot.openRoot(ROOT_0_ID);
+ assertDefaultContentOfTestDir0();
String query = "chocolate";
- mEnv.bot().openSearchView();
- mEnv.bot().setSearchQuery(query);
+ bot.openSearchView();
+ bot.setSearchQuery(query);
- mEnv.device().pressEnter();
- mEnv.device().pressBack();
- mEnv.assertDefaultContentOfTestDir0();
+ device.pressEnter();
+ device.pressBack();
+
+ device.waitForIdle();
+ assertDefaultContentOfTestDir0();
}
public void testSearchResultsFound_ClearsOnDirectoryChange() throws Exception {
- mEnv.initTestFiles();
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.assertDefaultContentOfTestDir0();
+ initTestFiles();
+ bot.openRoot(ROOT_0_ID);
+ assertDefaultContentOfTestDir0();
- String query = UiTestEnvironment.fileName1;
- mEnv.bot().openSearchView();
- mEnv.bot().setSearchQuery(query);
+ String query = fileName1;
+ bot.openSearchView();
+ bot.setSearchQuery(query);
- mEnv.device().pressEnter();
+ device.pressEnter();
- mEnv.bot().openRoot(ROOT_1_ID);
- mEnv.assertDefaultContentOfTestDir1();
+ bot.openRoot(ROOT_1_ID);
+ assertDefaultContentOfTestDir1();
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.assertDefaultContentOfTestDir0();
+ bot.openRoot(ROOT_0_ID);
+ assertDefaultContentOfTestDir0();
}
public void testSearchIconVisible_RootWithSearchSupport() throws Exception {
- mEnv.bot().openRoot(ROOT_0_ID);
- mEnv.bot().assertSearchTextFiledAndIcon(false, true);
+ bot.openRoot(ROOT_0_ID);
+ bot.assertSearchTextFiledAndIcon(false, true);
}
public void testSearchIconHidden_RootNoSearchSupport() throws Exception {
- mEnv.bot().openRoot(ROOT_1_ID);
- mEnv.bot().assertSearchTextFiledAndIcon(false, false);
+ bot.openRoot(ROOT_1_ID);
+ bot.assertSearchTextFiledAndIcon(false, false);
}
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
index d609fa84..417fd24 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java
@@ -47,8 +47,10 @@
* programmatically, and making assertions against the state of the UI.
*/
class UiBot {
+ public static final String TARGET_PKG = "com.android.documentsui";
private static final String TAG = "UiBot";
+ private static final String LAUNCHER_PKG = "com.android.launcher";
private static final BySelector SNACK_DELETE =
By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));
@@ -390,4 +392,13 @@
}
}
+ void revealLauncher() {
+ mDevice.pressHome();
+ mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), mTimeout);
+ }
+
+ void revealApp() {
+ mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), mTimeout);
+ mDevice.waitForIdle();
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiTestEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiTestEnvironment.java
deleted file mode 100644
index 9e30589..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiTestEnvironment.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * 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 com.android.documentsui.StubProvider.DEFAULT_AUTHORITY;
-import static com.android.documentsui.StubProvider.ROOT_0_ID;
-import static com.android.documentsui.StubProvider.ROOT_1_ID;
-
-import android.app.Instrumentation;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.RemoteException;
-import android.provider.DocumentsContract.Document;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Configurator;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.Until;
-import android.view.MotionEvent;
-
-import com.android.documentsui.model.RootInfo;
-
-/**
- * Provides basic test environment for UI tests:
- * - Launches activity
- * - Creates and gives access to test root directories and test files
- * - Cleans up the test environment
- */
-class UiTestEnvironment {
-
- public static final int TIMEOUT = 5000;
- public static final String NO_RENAME = "NO_RENAME";
-
- public static final String dirName1 = "Dir1";
- public static final String fileName1 = "file1.log";
- public static final String fileName2 = "file12.png";
- public static final String fileName3 = "anotherFile0.log";
- public static final String fileName4 = "poodles.text";
- public static final String fileNameNoRename = NO_RENAME + "file.txt";
-
- private static final String TARGET_PKG = "com.android.documentsui";
- private static final String LAUNCHER_PKG = "com.android.launcher";
-
- private final UiBot mBot;
- private final UiDevice mDevice;
- private final Context mContext;
-
- private RootInfo mRootDir0;
- private RootInfo mRootDir1;
- private int mDocsCountDir0;
- private int mDocsCountDir1;
-
- private ContentResolver mResolver;
- private DocumentsProviderHelper mDocsHelper;
- private ContentProviderClient mClient;
-
- private final Instrumentation mInstrumentation;
-
- public UiTestEnvironment(Instrumentation instrumentation) {
- mInstrumentation = instrumentation;
- mDevice = UiDevice.getInstance(mInstrumentation);
- // NOTE: Must be the "target" context, else security checks in content provider will fail.
- mContext = mInstrumentation.getTargetContext();
- mBot = new UiBot(mDevice, mContext, TIMEOUT);
- }
-
-/**
- * Launches default activity and waits for the application to appear.
- * @throws Exception
- */
- public void launch() throws Exception {
- Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
- launch(intent);
- }
-
- /**
- * Launches activity specified by intent and waits for the application to appear.
- * @param intent Intent describing activity to launch.
- * @throws Exception
- */
- public void launch(Intent intent) throws Exception {
- Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
- // Start from the home screen.
- mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT);
-
- mResolver = mContext.getContentResolver();
- mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY);
- mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient);
-
- // Launch app.
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- // Wait for the app to appear.
- mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT);
- mDevice.waitForIdle();
-
- resetStorage(); // Just incase a test failed and tearDown didn't happen.
- }
-
- public void cleanUp() throws Exception {
- resetStorage();
- mClient.release();
- }
-
- public void resetStorage() throws RemoteException {
- mClient.call("clear", null, null);
- mDevice.waitForIdle();
- }
-
- public void initTestFiles() throws RemoteException {
- mRootDir0 = mDocsHelper.getRoot(ROOT_0_ID);
- mRootDir1 = mDocsHelper.getRoot(ROOT_1_ID);
-
- mDocsHelper.createFolder(mRootDir0, dirName1);
- mDocsHelper.createDocument(mRootDir0, "text/plain", fileName1);
- mDocsHelper.createDocument(mRootDir0, "image/png", fileName2);
- mDocsHelper.createDocumentWithFlags(mRootDir0.documentId, "text/plain", fileNameNoRename,
- Document.FLAG_SUPPORTS_WRITE);
- mDocsCountDir0 = 4;
-
- mDocsHelper.createDocument(mRootDir1, "text/plain", fileName3);
- mDocsHelper.createDocument(mRootDir1, "text/plain", fileName4);
- mDocsCountDir1 = 2;
- }
-
- public void assertDefaultContentOfTestDir0() throws UiObjectNotFoundException {
- bot().assertDocumentsCount(ROOT_0_ID, getDocumentsCountDir0());
- bot().assertHasDocuments(UiTestEnvironment.fileName1, UiTestEnvironment.fileName2,
- UiTestEnvironment.dirName1, UiTestEnvironment.fileNameNoRename);
- }
-
- public void assertDefaultContentOfTestDir1() throws UiObjectNotFoundException {
- bot().assertDocumentsCount(ROOT_1_ID, getDocumentsCountDir1());
- bot().assertHasDocuments(UiTestEnvironment.fileName3, UiTestEnvironment.fileName4);
- }
-
- public UiBot bot() {
- return mBot;
- }
-
- public UiDevice device() {
- return mDevice;
- }
-
- public Context context() {
- return mContext;
- }
-
- public RootInfo getRootDir0() {
- return mRootDir0;
- }
-
- public int getDocumentsCountDir0() {
- return mDocsCountDir0;
- }
-
- public int getDocumentsCountDir1() {
- return mDocsCountDir1;
- }
-}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 3faa8f4..6af492c 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -73,7 +73,6 @@
extraValuesList,
COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
EMPTY_ARGS,
- /* heuristic */ false,
COLUMN_DEVICE_ID);
database.setTransactionSuccessful();
return changed;
@@ -92,16 +91,13 @@
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
- final boolean heuristic;
final String mapColumn;
Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
switch (mMappingMode.get(parentDocumentId)) {
case MAP_BY_MTP_IDENTIFIER:
- heuristic = false;
mapColumn = COLUMN_STORAGE_ID;
break;
case MAP_BY_NAME:
- heuristic = true;
mapColumn = Document.COLUMN_DISPLAY_NAME;
break;
default:
@@ -120,7 +116,6 @@
extraValuesList,
COLUMN_PARENT_DOCUMENT_ID + "=?",
strings(parentDocumentId),
- heuristic,
mapColumn);
database.setTransactionSuccessful();
@@ -137,16 +132,13 @@
* @param documents List of document information.
*/
synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
- final boolean heuristic;
final String mapColumn;
Preconditions.checkState(mMappingMode.containsKey(parentId));
switch (mMappingMode.get(parentId)) {
case MAP_BY_MTP_IDENTIFIER:
- heuristic = false;
mapColumn = COLUMN_OBJECT_HANDLE;
break;
case MAP_BY_NAME:
- heuristic = true;
mapColumn = Document.COLUMN_DISPLAY_NAME;
break;
default:
@@ -163,17 +155,13 @@
null,
COLUMN_PARENT_DOCUMENT_ID + "=?",
strings(parentId),
- heuristic,
mapColumn);
}
- @VisibleForTesting
void clearMapping() {
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
- mDatabase.deleteDocumentsAndRootsRecursively(
- COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_PENDING));
final ContentValues values = new ContentValues();
values.putNull(COLUMN_OBJECT_HANDLE);
values.putNull(COLUMN_STORAGE_ID);
@@ -209,11 +197,6 @@
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
- // Delete all pending rows.
- mDatabase.deleteDocumentsAndRootsRecursively(
- selection + " AND " + COLUMN_ROW_STATE + "=?",
- DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_PENDING)));
-
// Set all documents as invalidated.
final ContentValues values = new ContentValues();
values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
@@ -245,7 +228,6 @@
* @param rootExtraValuesList Values for root extra to be stored in the database.
* @param selection SQL where closure to select rows that shares the same parent.
* @param args Argument for selection SQL.
- * @param heuristic Whether the mapping mode is heuristic.
* @return Whether the method adds new rows.
*/
private boolean putDocuments(
@@ -253,7 +235,6 @@
@Nullable ContentValues[] rootExtraValuesList,
String selection,
String[] args,
- boolean heuristic,
String mappingKey) {
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
boolean added = false;
@@ -285,7 +266,7 @@
if (candidateCursor.getCount() == 0) {
rowId = database.insert(TABLE_DOCUMENTS, null, values);
added = true;
- } else if (!heuristic) {
+ } else {
candidateCursor.moveToNext();
rowId = candidateCursor.getLong(0);
database.update(
@@ -293,9 +274,6 @@
values,
SELECTION_DOCUMENT_ID,
strings(rowId));
- } else {
- values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING);
- rowId = database.insertOrThrow(TABLE_DOCUMENTS, null, values);
}
// Document ID is a primary integer key of the table. So the returned row
// IDs should be same with the document ID.
@@ -334,84 +312,10 @@
selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
args = EMPTY_ARGS;
}
- final String groupKey;
- switch (mMappingMode.get(parentId)) {
- case MAP_BY_MTP_IDENTIFIER:
- groupKey = parentId != null ? COLUMN_OBJECT_HANDLE : COLUMN_STORAGE_ID;
- break;
- case MAP_BY_NAME:
- groupKey = Document.COLUMN_DISPLAY_NAME;
- break;
- default:
- throw new Error("Unexpected mapping state.");
- }
mMappingMode.remove(parentId);
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
- // Get 1-to-1 mapping of invalidated document and pending document.
- final String invalidatedIdQuery = createStateFilter(
- ROW_STATE_INVALIDATED, Document.COLUMN_DOCUMENT_ID);
- final String pendingIdQuery = createStateFilter(
- ROW_STATE_PENDING, Document.COLUMN_DOCUMENT_ID);
- // SQL should be like:
- // SELECT group_concat(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END),
- // group_concat(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END)
- // WHERE device_id = ? AND parent_document_id IS NULL
- // GROUP BY display_name
- // HAVING count(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END) = 1 AND
- // count(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END) = 1
- final Cursor mergingCursor = database.query(
- TABLE_DOCUMENTS,
- new String[] {
- "group_concat(" + invalidatedIdQuery + ")",
- "group_concat(" + pendingIdQuery + ")"
- },
- selection,
- args,
- groupKey,
- "count(" + invalidatedIdQuery + ") = 1 AND count(" + pendingIdQuery + ") = 1",
- null);
-
- final ContentValues values = new ContentValues();
- while (mergingCursor.moveToNext()) {
- final String invalidatedId = mergingCursor.getString(0);
- final String pendingId = mergingCursor.getString(1);
-
- // Obtain the new values including the latest object handle from mapping row.
- getFirstRow(
- TABLE_DOCUMENTS,
- SELECTION_DOCUMENT_ID,
- new String[] { pendingId },
- values);
- values.remove(Document.COLUMN_DOCUMENT_ID);
- values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
- database.update(
- TABLE_DOCUMENTS,
- values,
- SELECTION_DOCUMENT_ID,
- new String[] { invalidatedId });
-
- getFirstRow(
- TABLE_ROOT_EXTRA,
- SELECTION_ROOT_ID,
- new String[] { pendingId },
- values);
- if (values.size() > 0) {
- values.remove(Root.COLUMN_ROOT_ID);
- database.update(
- TABLE_ROOT_EXTRA,
- values,
- SELECTION_ROOT_ID,
- new String[] { invalidatedId });
- }
-
- // Delete 'pending' row.
- mDatabase.deleteDocumentsAndRootsRecursively(
- SELECTION_DOCUMENT_ID, new String[] { pendingId });
- }
- mergingCursor.close();
-
boolean changed = false;
// Delete all invalidated rows that cannot be mapped.
@@ -421,58 +325,10 @@
changed = true;
}
- // The database cannot find old document ID for the pending rows.
- // Turn the all pending rows into valid state, which means the rows become to be
- // valid with new document ID.
- values.clear();
- values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
- if (database.update(
- TABLE_DOCUMENTS,
- values,
- COLUMN_ROW_STATE + " = ? AND " + selection,
- DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_PENDING), args)) != 0) {
- changed = true;
- }
database.setTransactionSuccessful();
return changed;
} finally {
database.endTransaction();
}
}
-
- /**
- * Obtains values of the first row for the query.
- * @param values ContentValues that the values are stored to.
- * @param table Target table.
- * @param selection Query to select rows.
- * @param args Argument for query.
- */
- private void getFirstRow(String table, String selection, String[] args, ContentValues values) {
- final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
- values.clear();
- final Cursor cursor = database.query(table, null, selection, args, null, null, null, "1");
- try {
- if (cursor.getCount() == 0) {
- return;
- }
- cursor.moveToNext();
- DatabaseUtils.cursorRowToContentValues(cursor, values);
- } finally {
- cursor.close();
- }
- }
-
- /**
- * Gets SQL expression that represents the given value or NULL depends on the row state.
- * You must pass static constants to this methods otherwise you may be suffered from SQL
- * injections.
- * @param state Expected row state.
- * @param a SQL value.
- * @return Expression that represents a if the row state is expected one, and represents NULL
- * otherwise.
- */
- private static String createStateFilter(int state, String a) {
- return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
- " THEN " + a + " ELSE NULL END";
- }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index 3cfb82f..33687cb 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -78,14 +78,6 @@
static final int ROW_STATE_INVALIDATED = 1;
/**
- * The state represents the raw has a valid object handle but it may be going to be mapped with
- * another rows invalidated. After fetching all documents under the parent, the database tries
- * to map the pending documents and the invalidated documents in order to keep old document ID
- * alive.
- */
- static final int ROW_STATE_PENDING = 2;
-
- /**
* Mapping mode that uses MTP identifier to find corresponding rows.
*/
static final int MAP_BY_MTP_IDENTIFIER = 0;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index a49dc67..a75012e 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -310,14 +310,14 @@
assertEquals(3, cursor.getCount());
cursor.moveToNext();
assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
- assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
@@ -333,7 +333,7 @@
assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
@@ -387,7 +387,7 @@
assertEquals(4, cursor.getCount());
cursor.moveToPosition(3);
- assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
@@ -406,7 +406,7 @@
assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
@@ -544,7 +544,7 @@
assertEquals(1, cursor.getCount());
cursor.moveToNext();
assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
- assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals(201, getInt(cursor, COLUMN_OBJECT_HANDLE));
cursor.close();
}
}
@@ -626,11 +626,12 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ // One reuses exisitng document ID 1.
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index e8e607b..c26b549 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -13,14 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label">Shell</string>
<!-- Title of notification indicating a bugreport is being generated. [CHAR LIMIT=50] -->
- <string name="bugreport_in_progress_title">Bug report is being generated</string>
+ <string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
<!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
- <string name="bugreport_finished_title">Bug report captured</string>
+ <string name="bugreport_finished_title">Bug report <xliff:g id="id">#%d</xliff:g> captured</string>
<!-- Title of notification indicating a bugreport is being updated before it can be shared. [CHAR LIMIT=50] -->
<string name="bugreport_updating_title">Adding details to the bug report</string>
<!-- Content notification indicating a bugreport is being updated before it can be shared, asking the user to wait [CHAR LIMIT=50] -->
@@ -53,12 +52,12 @@
<string name="bugreport_screenshot_action">Screenshot</string>
<!-- Toast message sent when the a screenshot for the bug report was taken successfully. -->
- <string name="bugreport_screenshot_taken">Screenshot taken succesfully.</string>
+ <string name="bugreport_screenshot_taken">Screenshot taken successfully.</string>
<!-- Toast message sent when the a screenshot for the bug report was not taken due to an error. -->
<string name="bugreport_screenshot_failed">Screenshot could not be taken.</string>
<!-- Title of the dialog asking for user-defined bug report details like name, title, and description. -->
- <string name="bugreport_info_dialog_title">Bug report details</string>
+ <string name="bugreport_info_dialog_title">Bug report <xliff:g id="id">#%d</xliff:g> details</string>
<!-- Text of the hint asking for the bug report name, which when set will define a suffix in the
bug report file names. [CHAR LIMIT=30] -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 78197b3..8c555a6 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -438,7 +438,7 @@
mContext.getString(R.string.bugreport_screenshot_action),
screenshotPendingIntent).build();
- final String title = mContext.getString(R.string.bugreport_in_progress_title);
+ final String title = mContext.getString(R.string.bugreport_in_progress_title, info.pid);
final String name =
info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
@@ -880,7 +880,7 @@
shareIntent.putExtra(EXTRA_PID, info.pid);
shareIntent.putExtra(EXTRA_INFO, info);
- final String title = context.getString(R.string.bugreport_finished_title);
+ final String title = context.getString(R.string.bugreport_finished_title, info.pid);
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentTitle(title)
@@ -1232,7 +1232,7 @@
mDialog = new AlertDialog.Builder(context)
.setView(view)
- .setTitle(context.getString(R.string.bugreport_info_dialog_title))
+ .setTitle(context.getString(R.string.bugreport_info_dialog_title, pid))
.setCancelable(false)
.setPositiveButton(context.getString(com.android.internal.R.string.ok),
null)
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index ea85c61..8c62670 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -36,6 +36,7 @@
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
+import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
@@ -58,6 +59,7 @@
import android.service.notification.StatusBarNotification;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
@@ -113,7 +115,7 @@
private static final String NO_NAME = null;
private static final String NO_SCREENSHOT = null;
private static final String NO_TITLE = null;
- private static final Integer NO_PID = null;
+ private static final int NO_PID = 0;
private static final boolean RENAMED_SCREENSHOTS = true;
private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
@@ -159,16 +161,20 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- assertProgressNotification(NAME, "0.00%");
+ final NumberFormat nf = NumberFormat.getPercentInstance();
+ nf.setMinimumFractionDigits(2);
+ nf.setMaximumFractionDigits(2);
+
+ assertProgressNotification(NAME, nf.format(0));
SystemProperties.set(PROGRESS_PROPERTY, "108");
- assertProgressNotification(NAME, "10.80%");
+ assertProgressNotification(NAME, nf.format(0.108));
SystemProperties.set(PROGRESS_PROPERTY, "500");
- assertProgressNotification(NAME, "50.00%");
+ assertProgressNotification(NAME, nf.format(0.50));
SystemProperties.set(MAX_PROPERTY, "2000");
- assertProgressNotification(NAME, "25.00%");
+ assertProgressNotification(NAME, nf.format(0.25));
Bundle extras =
sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
@@ -198,11 +204,11 @@
sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
if (serviceDies) {
- waitShareNotification();
+ waitShareNotification(PID);
killService();
}
- Bundle extras = acceptBugreportAndGetSharedIntent();
+ Bundle extras = acceptBugreportAndGetSharedIntent(PID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 2, RENAMED_SCREENSHOTS);
@@ -222,7 +228,7 @@
sendBugreportStarted(1000);
sendBugreportFinished(PID, mPlainTextPath, NO_SCREENSHOT);
- waitShareNotification();
+ waitShareNotification(PID);
// There's no indication in the UI about the screenshot finish, so just sleep like a baby...
Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
@@ -231,7 +237,7 @@
killService();
}
- Bundle extras = acceptBugreportAndGetSharedIntent();
+ Bundle extras = acceptBugreportAndGetSharedIntent(PID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 1, RENAMED_SCREENSHOTS);
@@ -243,7 +249,7 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- DetailsUi detailsUi = new DetailsUi(mUiBot);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
// Check initial name.
String actualName = detailsUi.nameField.getText().toString();
@@ -296,7 +302,7 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- DetailsUi detailsUi = new DetailsUi(mUiBot);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
// Check initial name.
String actualName = detailsUi.nameField.getText().toString();
@@ -326,7 +332,7 @@
sendBugreportStarted(1000);
waitForScreenshotButtonEnabled(true);
- DetailsUi detailsUi = new DetailsUi(mUiBot);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
detailsUi.nameField.setText("");
detailsUi.titleField.setText("");
@@ -363,14 +369,14 @@
waitForScreenshotButtonEnabled(true);
}
- DetailsUi detailsUi = new DetailsUi(mUiBot);
+ DetailsUi detailsUi = new DetailsUi(mUiBot, PID);
// Finish the bugreport while user's still typing the name.
detailsUi.nameField.setText(NEW_NAME);
sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
// Wait until the share notification is received...
- mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
+ waitShareNotification(PID);
// ...then close notification bar.
mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
@@ -384,7 +390,7 @@
detailsUi.clickOk();
// Finally, share bugreport.
- Bundle extras = acceptBugreportAndGetSharedIntent();
+ Bundle extras = acceptBugreportAndGetSharedIntent(PID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
NAME, TITLE, mDescription, 1, RENAMED_SCREENSHOTS);
@@ -397,7 +403,7 @@
// Send notification and click on share.
sendBugreportFinished(NO_PID, mPlainTextPath, null);
- acceptBugreport();
+ acceptBugreport(NO_PID);
// Handle the warning
mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
@@ -421,7 +427,7 @@
public void testShareBugreportAfterServiceDies() throws Exception {
sendBugreportFinished(NO_PID, mPlainTextPath, NO_SCREENSHOT);
killService();
- Bundle extras = acceptBugreportAndGetSharedIntent();
+ Bundle extras = acceptBugreportAndGetSharedIntent(NO_PID);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
}
@@ -457,14 +463,14 @@
private void assertProgressNotification(String name, String percent) {
// TODO: it currently looks for 3 distinct objects, without taking advantage of their
// relationship.
- openProgressNotification();
+ openProgressNotification(PID);
Log.v(TAG, "Looking for progress notification details: '" + name + "-" + percent + "'");
mUiBot.getObject(name);
mUiBot.getObject(percent);
}
- private void openProgressNotification() {
- String title = mContext.getString(R.string.bugreport_in_progress_title);
+ private void openProgressNotification(int pid) {
+ String title = mContext.getString(R.string.bugreport_in_progress_title, pid);
Log.v(TAG, "Looking for progress notification title: '" + title + "'");
mUiBot.getNotification(title);
}
@@ -494,7 +500,7 @@
*/
private Bundle sendBugreportFinishedAndGetSharedIntent(String bugreportPath,
String screenshotPath) {
- return sendBugreportFinishedAndGetSharedIntent(null, bugreportPath, screenshotPath);
+ return sendBugreportFinishedAndGetSharedIntent(NO_PID, bugreportPath, screenshotPath);
}
/**
@@ -502,10 +508,10 @@
*
* @return extras sent in the shared intent.
*/
- private Bundle sendBugreportFinishedAndGetSharedIntent(Integer pid, String bugreportPath,
+ private Bundle sendBugreportFinishedAndGetSharedIntent(int pid, String bugreportPath,
String screenshotPath) {
sendBugreportFinished(pid, bugreportPath, screenshotPath);
- return acceptBugreportAndGetSharedIntent();
+ return acceptBugreportAndGetSharedIntent(pid);
}
/**
@@ -513,8 +519,8 @@
*
* @return extras sent in the shared intent.
*/
- private Bundle acceptBugreportAndGetSharedIntent() {
- acceptBugreport();
+ private Bundle acceptBugreportAndGetSharedIntent(int pid) {
+ acceptBugreport(pid);
mUiBot.chooseActivity(UI_NAME);
return mListener.getExtras();
}
@@ -522,24 +528,24 @@
/**
* Waits for the notification to share the finished bugreport.
*/
- private void waitShareNotification() {
- mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
+ private void waitShareNotification(int pid) {
+ mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title, pid));
}
/**
* Accepts the notification to share the finished bugreport.
*/
- private void acceptBugreport() {
- mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title));
+ private void acceptBugreport(int pid) {
+ mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, pid));
}
/**
* Sends a "bugreport finished" intent.
*/
- private void sendBugreportFinished(Integer pid, String bugreportPath, String screenshotPath) {
+ private void sendBugreportFinished(int pid, String bugreportPath, String screenshotPath) {
Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- if (pid != null) {
+ if (pid != NO_PID) {
intent.putExtra(EXTRA_PID, pid);
}
if (bugreportPath != null) {
@@ -775,7 +781,7 @@
* Gets the notification button used to take a screenshot.
*/
private UiObject getScreenshotButton() {
- openProgressNotification();
+ openProgressNotification(PID);
return mUiBot.getVisibleObject(
mContext.getString(R.string.bugreport_screenshot_action).toUpperCase());
}
@@ -827,12 +833,15 @@
/**
* Gets the UI objects by opening the progress notification and clicking DETAILS.
*/
- DetailsUi(UiBot uiBot) {
- openProgressNotification();
+ DetailsUi(UiBot uiBot, int pid) throws UiObjectNotFoundException {
+ openProgressNotification(pid);
detailsButton = mUiBot.getVisibleObject(
mContext.getString(R.string.bugreport_info_action).toUpperCase());
mUiBot.click(detailsButton, "details_button");
// TODO: unhardcode resource ids
+ UiObject dialogTitle = mUiBot.getVisibleObjectById("android:id/alertTitle");
+ assertEquals("Wrong title", mContext.getString(R.string.bugreport_info_dialog_title,
+ pid), dialogTitle.getText().toString());
nameField = mUiBot.getVisibleObjectById("com.android.shell:id/name");
titleField = mUiBot.getVisibleObjectById("com.android.shell:id/title");
descField = mUiBot.getVisibleObjectById("com.android.shell:id/description");
@@ -849,7 +858,7 @@
}
void reOpen() {
- openProgressNotification();
+ openProgressNotification(PID);
mUiBot.click(detailsButton, "details_button");
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e5e5710..6702cef 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -282,9 +282,6 @@
<!-- The padding between freeform workspace tasks -->
<dimen name="recents_freeform_workspace_task_padding">8dp</dimen>
- <!-- Space reserved for the cards behind the top card in the top stack -->
- <dimen name="top_stack_peek_amount">12dp</dimen>
-
<!-- Space reserved for the cards behind the top card in the bottom stack -->
<dimen name="bottom_stack_peek_amount">12dp</dimen>
@@ -295,9 +292,6 @@
<!-- The height of the area before the bottom stack in which the notifications slow down -->
<dimen name="bottom_stack_slow_down_length">12dp</dimen>
- <!-- The height of the area before the top stack in which the notifications slow down -->
- <dimen name="top_stack_slow_down_length">12dp</dimen>
-
<!-- Z distance between notifications if they are in the stack -->
<dimen name="z_distance_between_notifications">0.5dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 3f482c8..9d4f425 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -205,7 +205,7 @@
? stack.indexOfStackTask(launchTarget)
: 0;
boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
- boolean animateNavBarScrim = true;
+ boolean animateNavBarScrim = !launchState.launchedWhileDocking;
mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
// Keep track of whether we launched from the nav bar button or via alt-tab
@@ -240,11 +240,11 @@
/**
* Dismisses recents if we are already visible and the intent is to toggle the recents view.
*/
- boolean dismissRecentsToFocusedTask() {
+ boolean dismissRecentsToFocusedTask(int logCategory) {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we have a focused Task, launch that Task now
- if (mRecentsView.launchFocusedTask()) return true;
+ if (mRecentsView.launchFocusedTask(logCategory)) return true;
}
return false;
}
@@ -270,7 +270,7 @@
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// If we have a focused Task, launch that Task now
- if (mRecentsView.launchFocusedTask()) return true;
+ if (mRecentsView.launchFocusedTask(0 /* logCategory */)) return true;
// If none of the other cases apply, then just go Home
dismissRecentsToHome(true /* animateTaskViews */);
return true;
@@ -360,7 +360,7 @@
mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
@Override
public void run() {
- dismissRecentsToFocusedTask();
+ dismissRecentsToFocusedTask(MetricsEvent.OVERVIEW_SELECT_TIMEOUT);
}
});
@@ -460,13 +460,7 @@
// wait on the system to send a signal that was never queued.
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = false;
- launchState.launchedFromSearchHome = false;
- launchState.launchedFromAppWithThumbnail = false;
- launchState.launchedToTaskId = -1;
- launchState.launchedWithAltTab = false;
- launchState.launchedHasConfigurationChanged = false;
- launchState.launchedViaDragGesture = false;
+ launchState.reset();
MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
}
@@ -640,7 +634,7 @@
// Focus the next task
EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration));
- MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
+ MetricsLogger.action(this, MetricsEvent.OVERVIEW_PAGE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 177e841..82e7861 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -34,10 +34,22 @@
public boolean launchedReuseTaskStackViews;
public boolean launchedHasConfigurationChanged;
public boolean launchedViaDragGesture;
+ public boolean launchedWhileDocking;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
+ public void reset() {
+ launchedFromHome = false;
+ launchedFromSearchHome = false;
+ launchedFromAppWithThumbnail = false;
+ launchedToTaskId = -1;
+ launchedWithAltTab = false;
+ launchedHasConfigurationChanged = false;
+ launchedViaDragGesture = false;
+ launchedWhileDocking = false;
+ }
+
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
@@ -46,6 +58,7 @@
// Set this flag to indicate that the configuration has changed since Recents last launched
launchedHasConfigurationChanged = true;
launchedViaDragGesture = false;
+ launchedWhileDocking = false;
}
/**
@@ -53,8 +66,9 @@
*/
public int getInitialFocusTaskIndex(int numTasks) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (launchedFromAppWithThumbnail) {
- if (debugFlags.isFastToggleRecentsEnabled()) {
+ if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
// If fast toggling, focus the front most task so that the next tap will focus the
// N-1 task
return numTasks - 1;
@@ -63,7 +77,7 @@
// If coming from another app, focus the next task
return numTasks - 2;
} else {
- if (debugFlags.isFastToggleRecentsEnabled()) {
+ if (!launchState.launchedWithAltTab && debugFlags.isFastToggleRecentsEnabled()) {
// If fast toggling, defer focusing until the next tap (which will automatically
// focus the front most task)
return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index dd7b7c1..5890b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -161,6 +161,7 @@
boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
boolean mReloadTasks;
+ boolean mLaunchedWhileDocking;
// Task launching
Rect mSearchBarBounds = new Rect();
@@ -270,10 +271,10 @@
}
public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
- boolean animate, boolean reloadTasks) {
+ boolean animate, boolean launchedWhileDockingTask) {
mTriggeredFromAltTab = triggeredFromAltTab;
mDraggingInRecents = draggingInRecents;
- mReloadTasks = reloadTasks;
+ mLaunchedWhileDocking = launchedWhileDockingTask;
if (mFastAltTabTrigger.hasTriggered()) {
// We are calling this from the doze trigger, so just fall through to show Recents
mFastAltTabTrigger.resetTrigger();
@@ -338,6 +339,7 @@
}
mDraggingInRecents = false;
+ mLaunchedWhileDocking = false;
mTriggeredFromAltTab = false;
try {
@@ -865,11 +867,11 @@
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
// should always preload the tasks now. If we are dragging in recents, reload them as
// the stacks might have changed.
- if (mReloadTasks || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
// Create a new load plan if preloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
- if (mReloadTasks || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+ if (mLaunchedWhileDocking || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, topTask.id, isTopTaskHome);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
@@ -957,6 +959,7 @@
launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
launchState.launchedHasConfigurationChanged = false;
launchState.launchedViaDragGesture = mDraggingInRecents;
+ launchState.launchedWhileDocking = mLaunchedWhileDocking;
Intent intent = new Intent();
intent.setClassName(RECENTS_PACKAGE, mRecentsIntentActivityName);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 5eeda72..d7b9b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -161,7 +161,7 @@
ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
ActivityOptions.makeBasic());
- MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT,
+ MetricsLogger.action(v.getContext(), MetricsEvent.OVERVIEW_SELECT,
task.key.getComponent().toString());
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
index acad0ea..3d1ea8e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryItemTouchCallbacks.java
@@ -21,6 +21,7 @@
import android.support.v7.widget.helper.ItemTouchHelper;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
@@ -72,6 +73,8 @@
// Keep track of deletions by swiping within history
MetricsLogger.histogram(mContext, "overview_task_dismissed_source",
Constants.Metrics.DismissSourceHistorySwipeGesture);
+ MetricsLogger.action(mContext, MetricsEvent.OVERVIEW_DISMISS,
+ taskRow.task.key.getComponent().toString());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 4deea54..52043f4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -62,6 +62,8 @@
}
};
+ public static final RectFEvaluator RECTF_EVALUATOR = new RectFEvaluator();
+
/**
* @return the first parent walking up the view hierarchy that has the given class type.
*
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index fb86214..42ebfa9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -287,13 +287,7 @@
// wait on the system to send a signal that was never queued.
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- launchState.launchedFromHome = false;
- launchState.launchedFromSearchHome = false;
- launchState.launchedFromAppWithThumbnail = false;
- launchState.launchedToTaskId = -1;
- launchState.launchedWithAltTab = false;
- launchState.launchedHasConfigurationChanged = false;
- launchState.launchedViaDragGesture = false;
+ launchState.reset();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5e113b9..42aaa97 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -185,7 +185,8 @@
// If we are already occluded by the app, then just set the default background scrim now.
// Otherwise, defer until the enter animation completes to animate the scrim with the
// tasks for the home animation.
- if (launchState.launchedFromAppWithThumbnail || mStack.getTaskCount() == 0) {
+ if (launchState.launchedWhileDocking || launchState.launchedFromAppWithThumbnail
+ || mStack.getTaskCount() == 0) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
mBackgroundScrim.setAlpha(0);
@@ -247,13 +248,18 @@
}
/** Launches the focused task from the first stack if possible */
- public boolean launchFocusedTask() {
+ public boolean launchFocusedTask(int logEvent) {
if (mTaskStackView != null) {
Task task = mTaskStackView.getFocusedTask();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
INVALID_STACK_ID, false));
+
+ if (logEvent != 0) {
+ MetricsLogger.action(getContext(), logEvent,
+ task.key.getComponent().toString());
+ }
return true;
}
}
@@ -645,7 +651,8 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (!launchState.launchedFromAppWithThumbnail && mStack.getTaskCount() > 0) {
+ if (!launchState.launchedWhileDocking && !launchState.launchedFromAppWithThumbnail
+ && mStack.getTaskCount() > 0) {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 7eaa193..76972d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -23,6 +23,7 @@
import android.graphics.Path;
import android.graphics.RectF;
import android.view.View;
+import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.systemui.Interpolators;
@@ -34,6 +35,7 @@
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -81,9 +83,18 @@
private static final PathInterpolator EXIT_TO_HOME_ALPHA_INTERPOLATOR =
new PathInterpolator(0.4f, 0, 1f, 1f);
+ private static final PathInterpolator FOCUS_NEXT_TASK_INTERPOLATOR =
+ new PathInterpolator(0.4f, 0, 0, 1f);
+ private static final PathInterpolator FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR =
+ new PathInterpolator(0, 0, 0, 1f);
+ private static final PathInterpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
+ new PathInterpolator(0.4f, 0, 0.2f, 1f);
+
private TaskStackView mStackView;
private TaskViewTransform mTmpTransform = new TaskViewTransform();
+ private ArrayList<TaskViewTransform> mTmpCurrentTaskTransforms = new ArrayList<>();
+ private ArrayList<TaskViewTransform> mTmpFinalTaskTransforms = new ArrayList<>();
public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
mStackView = stackView;
@@ -418,4 +429,92 @@
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
}
+
+ /**
+ * Starts the animation to focus the next {@link TaskView} when paging through recents.
+ *
+ * @return whether or not this will trigger a scroll in the stack
+ */
+ public boolean startScrollToFocusedTaskAnimation(Task newFocusedTask,
+ boolean requestViewFocus) {
+ TaskStackLayoutAlgorithm stackLayout = mStackView.getStackAlgorithm();
+ TaskStackViewScroller stackScroller = mStackView.getScroller();
+ TaskStack stack = mStackView.getStack();
+
+ final float newScroll = stackLayout.getStackScrollForTask(newFocusedTask);
+ boolean willScrollToFront = newScroll > stackScroller.getStackScroll();
+ boolean willScroll = Float.compare(newScroll, stackScroller.getStackScroll()) != 0;
+
+ // Get the current set of task transforms
+ ArrayList<Task> stackTasks = stack.getStackTasks();
+ mStackView.getCurrentTaskTransforms(stackTasks, mTmpCurrentTaskTransforms);
+
+ // Pick up the newly visible views after the scroll
+ mStackView.bindVisibleTaskViews(newScroll);
+
+ // Update the internal state
+ stackLayout.setFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
+ stackScroller.setStackScroll(newScroll, null /* animation */);
+ mStackView.cancelDeferredTaskViewLayoutAnimation();
+
+ // Get the final set of task transforms
+ mStackView.getLayoutTaskTransforms(newScroll, stackTasks, mTmpFinalTaskTransforms);
+
+ // Focus the task view
+ TaskView newFocusedTaskView = mStackView.getChildViewForTask(newFocusedTask);
+ newFocusedTaskView.setFocusedState(true, requestViewFocus);
+
+ // Setup the end listener to return all the hidden views to the view pool after the
+ // focus animation
+ AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStackView.bindVisibleTaskViews(newScroll);
+ }
+ };
+
+ List<TaskView> taskViews = mStackView.getTaskViews();
+ int taskViewCount = taskViews.size();
+ int newFocusTaskViewIndex = taskViews.indexOf(newFocusedTaskView);
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ Task task = tv.getTask();
+
+ if (mStackView.isIgnoredTask(task)) {
+ continue;
+ }
+
+ int taskIndex = stackTasks.indexOf(task);
+ TaskViewTransform fromTransform = mTmpCurrentTaskTransforms.get(taskIndex);
+ TaskViewTransform toTransform = mTmpFinalTaskTransforms.get(taskIndex);
+
+ // Update the task to the initial state (for the newly picked up tasks)
+ mStackView.updateTaskViewToTransform(tv, fromTransform, AnimationProps.IMMEDIATE);
+
+ int duration;
+ Interpolator interpolator;
+ if (willScrollToFront) {
+ duration = Math.max(100, 100 + ((i - 1) * 50));
+ interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+ } else {
+ if (i < newFocusTaskViewIndex) {
+ duration = 150 + ((newFocusTaskViewIndex - i - 1) * 50);
+ interpolator = FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR;
+ } else if (i > newFocusTaskViewIndex) {
+ duration = Math.max(100, 150 - ((i - newFocusTaskViewIndex - 1) * 50));
+ interpolator = FOCUS_IN_FRONT_NEXT_TASK_INTERPOLATOR;
+ } else {
+ duration = 200;
+ interpolator = FOCUS_NEXT_TASK_INTERPOLATOR;
+ }
+ }
+
+ AnimationProps anim = new AnimationProps()
+ .setDuration(AnimationProps.BOUNDS, duration)
+ .setInterpolator(AnimationProps.BOUNDS, interpolator)
+ .setListener(endListener);
+ mStackView.updateTaskViewToTransform(tv, toTransform, anim);
+ }
+ return willScroll;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index bd37c3b..19ac1e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -457,7 +457,7 @@
launchTaskIndex - 1));
}
} else {
- float offsetPct = (float) (mTaskRect.height() / 2) / mStackRect.height();
+ float offsetPct = (float) (mTaskRect.height() / 3) / mStackRect.height();
float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
mInitialScrollP = Math.max(mMinScrollP, Math.min(mMaxScrollP,
launchTaskIndex - mUnfocusedRange.getAbsoluteX(normX)));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index bb74de4..fb3515a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,6 +20,8 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.ComponentName;
@@ -41,8 +43,12 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
@@ -113,6 +119,7 @@
private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
+ LayoutInflater mInflater;
TaskStack mStack;
TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScroller mStackScroller;
@@ -142,16 +149,15 @@
boolean mScreenPinningEnabled;
// The stable stack bounds are the full bounds that we were measured with from RecentsView
- Rect mStableStackBounds = new Rect();
+ private Rect mStableStackBounds = new Rect();
// The current stack bounds are dynamic and may change as the user drags and drops
- Rect mStackBounds = new Rect();
+ private Rect mStackBounds = new Rect();
- int[] mTmpVisibleRange = new int[2];
- Rect mTmpRect = new Rect();
- ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
- List<TaskView> mTmpTaskViews = new ArrayList<>();
- TaskViewTransform mTmpTransform = new TaskViewTransform();
- LayoutInflater mInflater;
+ private int[] mTmpVisibleRange = new int[2];
+ private Rect mTmpRect = new Rect();
+ private ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
+ private List<TaskView> mTmpTaskViews = new ArrayList<>();
+ private TaskViewTransform mTmpTransform = new TaskViewTransform();
// A convenience update listener to request updating clipping of tasks
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
@@ -396,6 +402,7 @@
int frontMostVisibleIndex = -1;
int backMostVisibleIndex = -1;
boolean useTargetStackScroll = Float.compare(curStackScroll, targetStackScroll) != 0;
+ boolean targetScrollIsInFront = targetStackScroll > curStackScroll;
// We can reuse the task transforms where possible to reduce object allocation
Utilities.matchTaskListSize(tasks, taskTransforms);
@@ -439,7 +446,7 @@
frontMostVisibleIndex = i;
}
backMostVisibleIndex = i;
- } else {
+ } else if (!targetScrollIsInFront) {
if (backMostVisibleIndex != -1) {
// We've reached the end of the visible range, so going down the rest of the
// stack, we can just reset the transforms accordingly
@@ -531,7 +538,7 @@
}
// Skip the invisible non-freeform stack tasks
- if (i > visibleStackRange[0] && !task.isFreeformTask()) {
+ if (!task.isFreeformTask() && !transform.visible) {
continue;
}
@@ -671,12 +678,20 @@
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i);
- mLayoutAlgorithm.getStackTransform(task, stackScroll, transform, null);
+ mLayoutAlgorithm.getStackTransform(task, stackScroll, transform, null,
+ true /* forceUpdate */);
transform.visible = true;
}
}
/**
+ * Cancels the next deferred task view layout.
+ */
+ void cancelDeferredTaskViewLayoutAnimation() {
+ mDeferredTaskViewLayoutAnimation = null;
+ }
+
+ /**
* Cancels all {@link TaskView} animations.
*
* @see #cancelAllTaskViewAnimations(ArraySet<Task.TaskKey>)
@@ -716,7 +731,7 @@
TaskView frontTv = null;
int clipBottom = 0;
- if (mIgnoreTasks.contains(tv.getTask().key)) {
+ if (isIgnoredTask(tv.getTask())) {
// For each of the ignore tasks, update the translationZ of its TaskView to be
// between the translationZ of the tasks immediately underneath it
if (prevVisibleTv != null) {
@@ -804,15 +819,15 @@
}
/**
- * Sets the focused task to the provided (bounded taskIndex).
+ * Sets the focused task to the provided (bounded focusTaskIndex).
*
* @return whether or not the stack will scroll as a part of this focus change
*/
- private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
- final boolean requestViewFocus, final int timerIndicatorDuration) {
+ private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
+ boolean requestViewFocus, int timerIndicatorDuration) {
// Find the next task to focus
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
- Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1;
+ Math.max(0, Math.min(mStack.getTaskCount() - 1, focusTaskIndex)) : -1;
final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
mStack.getStackTasks().get(newFocusedTaskIndex) : null;
@@ -830,7 +845,6 @@
}
boolean willScroll = false;
-
mFocusedTask = newFocusedTask;
if (newFocusedTask != null) {
@@ -845,33 +859,20 @@
}
}
- Runnable focusTaskRunnable = new Runnable() {
- @Override
- public void run() {
- final TaskView tv = getChildViewForTask(newFocusedTask);
- if (tv != null) {
- tv.setFocusedState(true, requestViewFocus);
- }
- }
- };
-
if (scrollToTask) {
// Cancel any running enter animations at this point when we scroll or change focus
if (!mEnterAnimationComplete) {
cancelAllTaskViewAnimations();
}
- // TODO: Center the newly focused task view, only if not freeform
- float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask);
- if (Float.compare(newScroll, mStackScroller.getStackScroll()) != 0) {
- mStackScroller.animateScroll(newScroll, focusTaskRunnable);
- willScroll = true;
- } else {
- focusTaskRunnable.run();
- }
- mLayoutAlgorithm.animateFocusState(TaskStackLayoutAlgorithm.STATE_FOCUSED);
+ willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
+ requestViewFocus);
} else {
- focusTaskRunnable.run();
+ // Focus the task view
+ TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
+ if (newFocusedTaskView != null) {
+ newFocusedTaskView.setFocusedState(true, requestViewFocus);
+ }
}
}
return willScroll;
@@ -1276,7 +1277,7 @@
Task task = tasks.get(i);
// Ignore deleting tasks
- if (mIgnoreTasks.contains(task.key)) {
+ if (isIgnoredTask(task)) {
if (i == tasks.size() - 1) {
isFrontMostTask.value = true;
}
@@ -1390,7 +1391,7 @@
}
@Override
- public void prepareViewToEnterPool(TaskView tv) {
+ public void onReturnViewToPool(TaskView tv) {
final Task task = tv.getTask();
// Report that this tasks's data is no longer being used
@@ -1411,7 +1412,7 @@
}
@Override
- public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
+ public void onPickUpViewFromPool(TaskView tv, Task task, boolean isNewView) {
// Find the index where this task should be placed in the stack
int taskIndex = mStack.indexOfStackTask(task);
int insertIndex = findTaskViewInsertIndex(task, taskIndex);
@@ -1601,6 +1602,9 @@
public final void onBusEvent(TaskViewDismissedEvent event) {
removeTaskViewFromStack(event.taskView, event.task);
EventBus.getDefault().send(new DeleteTaskDataEvent(event.task));
+
+ MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS,
+ event.task.key.getComponent().toString());
}
public final void onBusEvent(FocusNextTaskViewEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index c641d75..d1bce55 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -191,21 +191,27 @@
stopScroller();
stopBoundScrollAnimation();
- mFinalAnimatedScroll = newScroll;
- mScrollAnimator = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
- mScrollAnimator.setDuration(mContext.getResources().getInteger(
- R.integer.recents_animate_task_stack_scroll_duration));
- mScrollAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- mScrollAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (postRunnable != null) {
- postRunnable.run();
+ if (Float.compare(mStackScrollP, newScroll) != 0) {
+ mFinalAnimatedScroll = newScroll;
+ mScrollAnimator = ObjectAnimator.ofFloat(this, STACK_SCROLL, getStackScroll(), newScroll);
+ mScrollAnimator.setDuration(mContext.getResources().getInteger(
+ R.integer.recents_animate_task_stack_scroll_duration));
+ mScrollAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ mScrollAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (postRunnable != null) {
+ postRunnable.run();
+ }
+ mScrollAnimator.removeAllListeners();
}
- mScrollAnimator.removeAllListeners();
+ });
+ mScrollAnimator.start();
+ } else {
+ if (postRunnable != null) {
+ postRunnable.run();
}
- });
- mScrollAnimator.start();
+ }
}
/** Aborts any current stack scrolls */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index d6680fd..5d1bb66 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -34,6 +34,7 @@
import android.view.animation.PathInterpolator;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
@@ -58,8 +59,6 @@
class TaskStackViewTouchHandler implements SwipeHelper.Callback {
private static final int INACTIVE_POINTER_ID = -1;
-
- private static final RectFEvaluator RECT_EVALUATOR = new RectFEvaluator();
private static final Interpolator STACK_TRANSFORM_INTERPOLATOR =
new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f);
@@ -230,6 +229,8 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
+
+ MetricsLogger.action(mSv.getContext(), MetricsEvent.OVERVIEW_SCROLL);
}
}
if (mIsScrolling) {
@@ -542,8 +543,8 @@
mTmpTransform.copyFrom(fromTransform);
// We only really need to interpolate the bounds, progress and translation
- mTmpTransform.rect.set(RECT_EVALUATOR.evaluate(dismissFraction, fromTransform.rect,
- toTransform.rect));
+ mTmpTransform.rect.set(Utilities.RECTF_EVALUATOR.evaluate(dismissFraction,
+ fromTransform.rect, toTransform.rect));
mTmpTransform.p = fromTransform.p + (toTransform.p - fromTransform.p) * dismissFraction;
mTmpTransform.translationZ = fromTransform.translationZ +
(toTransform.translationZ - fromTransform.translationZ) * dismissFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 439d96f..850e36e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -38,6 +38,8 @@
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateInterpolator;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
@@ -585,6 +587,9 @@
}
EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID,
screenPinningRequested));
+
+ MetricsLogger.action(v.getContext(), MetricsEvent.OVERVIEW_SELECT,
+ mTask.key.getComponent().toString());
}
/**** View.OnLongClickListener Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
index 31fbd3e..a287fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
@@ -29,8 +29,8 @@
/* An interface to the consumer of a view pool */
public interface ViewPoolConsumer<V, T> {
public V createView(Context context);
- public void prepareViewToEnterPool(V v);
- public void prepareViewToLeavePool(V v, T prepareData, boolean isNewView);
+ public void onReturnViewToPool(V v);
+ public void onPickUpViewFromPool(V v, T prepareData, boolean isNewView);
public boolean hasPreferredData(V v, T preferredData);
}
@@ -46,7 +46,7 @@
/** Returns a view into the pool */
void returnViewToPool(V v) {
- mViewCreator.prepareViewToEnterPool(v);
+ mViewCreator.onReturnViewToPool(v);
mPool.push(v);
}
@@ -73,7 +73,7 @@
v = mPool.pop();
}
}
- mViewCreator.prepareViewToLeavePool(v, prepareData, isNewView);
+ mViewCreator.onPickUpViewFromPool(v, prepareData, isNewView);
return v;
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 12e2713..36cfac8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -73,6 +73,7 @@
private int mCurrentWidth;
private int mCurrentHeight;
private AnimatorSet mAnimator;
+ private boolean mTouching;
public DividerHandleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -86,6 +87,9 @@
}
public void setTouching(boolean touching, boolean animate) {
+ if (touching == mTouching) {
+ return;
+ }
if (mAnimator != null) {
mAnimator.cancel();
mAnimator = null;
@@ -103,6 +107,7 @@
animateToTarget(touching ? mCircleDiameter : mWidth,
touching ? mCircleDiameter : mHeight, touching);
}
+ mTouching = touching;
}
private void animateToTarget(int targetWidth, int targetHeight, boolean touching) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 83c22b1..1bdf5a1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -100,6 +100,7 @@
private final int[] mTempInt2 = new int[2];
private boolean mMoving;
private int mTouchSlop;
+ private boolean mBackgroundLifted;
private int mDividerInsets;
private int mDisplayWidth;
@@ -210,8 +211,8 @@
mDockSide = mWindowManagerProxy.getDockSide();
initializeSnapAlgorithm();
mWindowManagerProxy.setResizing(true);
- mWindowManager.setSlippery(false);
if (touching) {
+ mWindowManager.setSlippery(false);
liftBackground();
}
return mDockSide != WindowManager.DOCKED_INVALID;
@@ -389,6 +390,9 @@
}
private void liftBackground() {
+ if (mBackgroundLifted) {
+ return;
+ }
if (isHorizontalDivision()) {
mBackground.animate().scaleY(1.4f);
} else {
@@ -407,9 +411,13 @@
.setDuration(TOUCH_ANIMATION_DURATION)
.translationZ(mTouchElevation)
.start();
+ mBackgroundLifted = true;
}
private void releaseBackground() {
+ if (!mBackgroundLifted) {
+ return;
+ }
mBackground.animate()
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
@@ -422,6 +430,7 @@
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
.translationZ(0)
.start();
+ mBackgroundLifted = false;
}
@Override
@@ -485,7 +494,9 @@
}
// Make sure shadows are updated
- mBackground.invalidate();
+ if (mBackground.getZ() > 0f) {
+ mBackground.invalidate();
+ }
mLastResizeRect.set(mDockedRect);
if (taskPosition != TASK_POSITION_SAME) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7f1316f..84b2031 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -947,6 +947,10 @@
}
}
+ public boolean mustStayOnScreen() {
+ return mIsHeadsUp;
+ }
+
private void updateClearability() {
// public versions cannot be dismissed
mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index a0fb34a..8042b60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -399,6 +399,10 @@
return false;
}
+ public boolean mustStayOnScreen() {
+ return false;
+ }
+
/**
* A listener notifying when {@link #getActualHeight} changes.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 5abd1d5..dd6d6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -35,6 +35,8 @@
import android.widget.SeekBar;
import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -51,6 +53,7 @@
private SeekBar mSeekBar;
private Notification.Topic mTopic;
private INotificationManager mINotificationManager;
+ private int mStartingImportance;
public NotificationGuts(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -103,6 +106,7 @@
void bindImportance(final StatusBarNotification sbn, final ExpandableNotificationRow row,
final int importance) {
+ mStartingImportance = importance;
mINotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
mTopic = sbn.getNotification().getTopic() == null
@@ -151,6 +155,7 @@
}
updateTitleAndSummary(progress);
if (fromUser) {
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_MODIFY_IMPORTANCE_SLIDER);
if (appUsesTopics) {
mApplyToTopic.setVisibility(View.VISIBLE);
mApplyToTopic.setText(
@@ -205,6 +210,8 @@
void saveImportance(final StatusBarNotification sbn) {
int progress = mSeekBar.getProgress();
+ MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
+ progress - mStartingImportance);
try {
mINotificationManager.setImportance(sbn.getPackageName(), sbn.getUid(),
mApplyToTopic.isChecked() ? mTopic : null, progress);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index b5b7f43..79c21f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -79,7 +79,8 @@
mTouchingHeadsUpView = false;
if (child instanceof ExpandableNotificationRow) {
mPickedChild = (ExpandableNotificationRow) child;
- mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned();
+ mTouchingHeadsUpView = !mStackScroller.isExpanded()
+ && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
}
break;
case MotionEvent.ACTION_POINTER_UP:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5f5974e..0febbd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -738,9 +738,9 @@
+ (offscreen ? " OFFSCREEN!" : ""));
pw.println(String.format(" mCurrentView: id=%s (%dx%d) %s",
- getResourceName(mCurrentView.getId()),
- mCurrentView.getWidth(), mCurrentView.getHeight(),
- visibilityToString(mCurrentView.getVisibility())));
+ getResourceName(getCurrentView().getId()),
+ getCurrentView().getWidth(), getCurrentView().getHeight(),
+ visibilityToString(getCurrentView().getVisibility())));
pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s",
mDisabledFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 09a7bf0..50a49a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -519,7 +519,7 @@
*/
protected boolean mStartedGoingToSleep;
- private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD
+ private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_HUN
| StackViewState.LOCATION_MAIN_AREA;
private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index cc0e67d..49e9c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -75,7 +75,7 @@
ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener {
public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
- private static final String TAG = "NotificationStackScrollLayout";
+ private static final String TAG = "StackScroller";
private static final boolean DEBUG = false;
private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
@@ -136,7 +136,7 @@
private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
private AmbientState mAmbientState = new AmbientState();
private NotificationGroupManager mGroupManager;
- private ArrayList<View> mChildrenToAddAnimated = new ArrayList<>();
+ private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
@@ -474,6 +474,7 @@
* modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
*/
private void updateChildren() {
+ updateScrollStateForAddedChildren();
mAmbientState.setScrollY(mOwnScrollY);
mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
@@ -483,6 +484,28 @@
}
}
+ private void updateScrollStateForAddedChildren() {
+ if (mChildrenToAddAnimated.isEmpty()) {
+ return;
+ }
+ for (int i = 0; i < getChildCount(); i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ if (mChildrenToAddAnimated.contains(child)) {
+ int startingPosition = getPositionInLinearLayout(child);
+ int padding = child.needsIncreasedPadding()
+ ? mIncreasedPaddingBetweenElements :
+ mPaddingBetweenElements;
+ int childHeight = getIntrinsicHeight(child) + padding;
+ if (startingPosition < mOwnScrollY) {
+ // This child starts off screen, so let's keep it offscreen to keep the others visible
+
+ mOwnScrollY += childHeight;
+ }
+ }
+ }
+ clampScrollPosition();
+ }
+
private void requestChildrenUpdate() {
if (!mChildrenUpdateRequested) {
getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
@@ -1648,12 +1671,17 @@
bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
bottom = Math.min(bottom, getHeight());
}
- } else if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
- top = mTopPadding;
+ } else {
+ top = (int) (mTopPadding + mStackTranslation);
bottom = top;
}
- mBackgroundBounds.top = Math.max(0, top);
- mBackgroundBounds.bottom = Math.min(getHeight(), bottom);
+ if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+ mBackgroundBounds.top = (int) Math.max(mTopPadding + mStackTranslation, top);
+ } else {
+ // otherwise the animation from the shade to the keyguard will jump as it's maxed
+ mBackgroundBounds.top = Math.max(0, top);
+ }
+ mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
}
private ActivatableNotificationView getFirstPinnedHeadsUp() {
@@ -3206,6 +3234,10 @@
}
}
+ public boolean isExpanded() {
+ return mIsExpanded;
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f6959f0..e87b363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -39,17 +39,14 @@
private static final String LOG_TAG = "StackScrollAlgorithm";
private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
- private static final int MAX_ITEMS_IN_TOP_STACK = 3;
private int mPaddingBetweenElements;
private int mIncreasedPaddingBetweenElements;
private int mCollapsedSize;
- private int mTopStackPeekSize;
private int mBottomStackPeekSize;
private int mZDistanceBetweenElements;
private int mZBasicHeight;
- private StackIndentationFunctor mTopStackIndentationFunctor;
private StackIndentationFunctor mBottomStackIndentationFunctor;
private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
@@ -58,12 +55,8 @@
private boolean mIsExpanded;
private ExpandableView mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
- private int mTopStackTotalSize;
private int mBottomStackSlowDownLength;
- private int mTopStackSlowDownLength;
private int mCollapseSecondCardPadding;
- private ExpandableView mFirstChild;
- private int mFirstChildMinHeight;
public StackScrollAlgorithm(Context context) {
initView(context);
@@ -71,22 +64,6 @@
public void initView(Context context) {
initConstants(context);
- updatePadding();
- }
-
- private void updatePadding() {
- mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
- + mTopStackPeekSize;
- mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
- MAX_ITEMS_IN_TOP_STACK,
- mTopStackPeekSize,
- mTopStackTotalSize - mTopStackPeekSize,
- 0.5f);
- mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
- MAX_ITEMS_IN_BOTTOM_STACK,
- mBottomStackPeekSize,
- getBottomStackSlowDownLength(),
- 0.5f);
}
public int getBottomStackSlowDownLength() {
@@ -100,8 +77,6 @@
.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
mCollapsedSize = context.getResources()
.getDimensionPixelSize(R.dimen.notification_min_height);
- mTopStackPeekSize = context.getResources()
- .getDimensionPixelSize(R.dimen.top_stack_peek_amount);
mBottomStackPeekSize = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mZDistanceBetweenElements = Math.max(1, context.getResources()
@@ -109,10 +84,13 @@
mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
mBottomStackSlowDownLength = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
- mTopStackSlowDownLength = context.getResources()
- .getDimensionPixelSize(R.dimen.top_stack_slow_down_length);
mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
R.dimen.notification_collapse_second_card_padding);
+ mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+ MAX_ITEMS_IN_BOTTOM_STACK,
+ mBottomStackPeekSize,
+ getBottomStackSlowDownLength(),
+ 0.5f);
}
public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -123,32 +101,13 @@
// First we reset the view states to their default values.
resultState.resetViewStates();
- algorithmState.itemsInTopStack = 0.0f;
- algorithmState.partialInTop = 0.0f;
- algorithmState.lastTopStackIndex = 0;
- algorithmState.scrolledPixelsTop = 0;
- algorithmState.itemsInBottomStack = 0.0f;
- algorithmState.partialInBottom = 0.0f;
- mFirstChildMinHeight = mFirstChild == null ? 0 : mFirstChild.getMinHeight();
- float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+ initAlgorithmState(resultState, algorithmState, ambientState);
- int scrollY = ambientState.getScrollY();
-
- // Due to the overScroller, the stackscroller can have negative scroll state. This is
- // already accounted for by the top padding and doesn't need an additional adaption
- scrollY = Math.max(0, scrollY);
- algorithmState.scrollY = (int) (scrollY + mFirstChildMinHeight + bottomOverScroll);
-
- initAlgorithmState(resultState, algorithmState);
-
- // Phase 1:
- findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);
-
- // Phase 2:
updatePositionsForState(resultState, algorithmState, ambientState);
- // Phase 3:
- updateZValuesForState(resultState, algorithmState);
+ updateZValuesForState(resultState, algorithmState, ambientState);
+
+ updateHeadsUpStates(resultState, algorithmState, ambientState);
handleDraggedViews(ambientState, resultState, algorithmState);
updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
@@ -185,6 +144,7 @@
private void updateClipping(StackScrollState resultState,
StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
boolean dismissAllInProgress = ambientState.isDismissAllInProgress();
+ float drawStart = ambientState.getTopPadding() + ambientState.getStackTranslation();
float previousNotificationEnd = 0;
float previousNotificationStart = 0;
boolean previousNotificationIsSwiped = false;
@@ -192,6 +152,10 @@
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackViewState state = resultState.getViewStateForView(child);
+ if (!child.mustStayOnScreen()) {
+ previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
+ previousNotificationStart = Math.max(drawStart, previousNotificationStart);
+ }
float newYTranslation = state.yTranslation;
float newHeight = state.height;
// apply clipping and shadow
@@ -222,7 +186,7 @@
} else {
previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
previousNotificationEnd = newNotificationEnd;
- previousNotificationStart = newYTranslation + state.clipTopAmount;
+ previousNotificationStart =newYTranslation + state.clipTopAmount;
}
}
}
@@ -314,8 +278,20 @@
/**
* Initialize the algorithm state like updating the visible children.
*/
- private void initAlgorithmState(StackScrollState resultState,
- StackScrollAlgorithmState state) {
+ private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+ AmbientState ambientState) {
+ state.itemsInBottomStack = 0.0f;
+ state.partialInBottom = 0.0f;
+ float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+
+ int scrollY = ambientState.getScrollY();
+
+ // Due to the overScroller, the stackscroller can have negative scroll state. This is
+ // already accounted for by the top padding and doesn't need an additional adaption
+ scrollY = Math.max(0, scrollY);
+ state.scrollY = (int) (scrollY + bottomOverScroll);
+
+ //now init the visible children and update paddings
ViewGroup hostView = resultState.getHostView();
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
@@ -383,15 +359,9 @@
float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
// The y coordinate of the current child.
- float currentYPosition = 0.0f;
-
- // How far in is the element currently transitioning into the bottom stack.
- float yPositionInScrollView = 0.0f;
+ float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
- int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f
- ? algorithmState.lastTopStackIndex
- : (int) algorithmState.itemsInTopStack;
int paddingAfterChild;
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
@@ -400,47 +370,16 @@
paddingAfterChild = getPaddingAfterChild(algorithmState, child);
int childHeight = getMaxAllowedChildHeight(child);
int minHeight = child.getMinHeight();
- float yPositionInScrollViewAfterElement = yPositionInScrollView
- + childHeight
- + paddingAfterChild;
- float scrollOffset = yPositionInScrollView - algorithmState.scrollY +
- mFirstChildMinHeight;
-
- if (i == algorithmState.lastTopStackIndex + 1) {
- // Normally the position of this child is the position in the regular scrollview,
- // but if the two stacks are very close to each other,
- // then have have to push it even more upwards to the position of the bottom
- // stack start.
- currentYPosition = Math.min(scrollOffset, bottomStackStart);
- }
childViewState.yTranslation = currentYPosition;
+ if (i == 0) {
+ updateFirstChildHeight(child, childViewState, childHeight, ambientState);
+ }
// The y position after this element
float nextYPosition = currentYPosition + childHeight +
paddingAfterChild;
-
- if (i <= algorithmState.lastTopStackIndex) {
+ if (nextYPosition >= bottomStackStart) {
// Case 1:
- // We are in the top Stack
- updateStateForTopStackChild(algorithmState, child,
- numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset);
- clampPositionToTopStackEnd(childViewState, childHeight);
-
- // check if we are overlapping with the bottom stack
- if (childViewState.yTranslation + childHeight + paddingAfterChild
- >= bottomStackStart && !mIsExpansionChanging && i != 0) {
- // we just collapse this element slightly
- int newSize = (int) Math.max(bottomStackStart - paddingAfterChild -
- childViewState.yTranslation, minHeight);
- childViewState.height = newSize;
- updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart,
- child, childViewState.yTranslation, childViewState,
- childHeight);
- }
- clampPositionToBottomStackStart(childViewState, childViewState.height,
- minHeight, ambientState);
- } else if (nextYPosition >= bottomStackStart) {
- // Case 2:
// We are in the bottom stack.
if (currentYPosition >= bottomStackStart) {
// According to the regular scroll view we are fully translated out of the
@@ -455,36 +394,30 @@
childViewState, childHeight);
}
} else {
- // Case 3:
+ // Case 2:
// We are in the regular scroll area.
childViewState.location = StackViewState.LOCATION_MAIN_AREA;
- clampYTranslation(childViewState, childHeight, ambientState);
+ clampPositionToBottomStackStart(childViewState, childViewState.height, childHeight,
+ ambientState);
}
- // The first card is always rendered.
- if (i == 0) {
- childViewState.hidden = false;
- childViewState.shadowAlpha = 1.0f;
- childViewState.yTranslation = Math.max(
- mFirstChildMinHeight - algorithmState.scrollY, 0);
- if (childViewState.yTranslation + childViewState.height
- > bottomPeekStart - mCollapseSecondCardPadding) {
- childViewState.height = (int) Math.max(
- bottomPeekStart - mCollapseSecondCardPadding
- - childViewState.yTranslation, mFirstChildMinHeight);
- }
- childViewState.location = StackViewState.LOCATION_FIRST_CARD;
+ if (i == 0 && ambientState.getScrollY() <= 0) {
+ // The first card can get into the bottom stack if it's the only one
+ // on the lockscreen which pushes it up. Let's make sure that doesn't happen and
+ // it stays at the top
+ childViewState.yTranslation = Math.max(0, childViewState.yTranslation);
+ }
+ currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
+ if (currentYPosition <= 0) {
+ childViewState.location = StackViewState.LOCATION_HIDDEN_TOP;
}
if (childViewState.location == StackViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
}
- currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
- yPositionInScrollView = yPositionInScrollViewAfterElement;
childViewState.yTranslation += ambientState.getTopPadding()
+ ambientState.getStackTranslation();
}
- updateHeadsUpStates(resultState, algorithmState, ambientState);
}
private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
@@ -506,24 +439,27 @@
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (!row.isHeadsUp()) {
break;
- } else if (topHeadsUpEntry == null) {
- topHeadsUpEntry = row;
}
StackViewState childState = resultState.getViewStateForView(row);
+ if (topHeadsUpEntry == null) {
+ topHeadsUpEntry = row;
+ childState.location = StackViewState.LOCATION_FIRST_HUN;
+ }
boolean isTopEntry = topHeadsUpEntry == row;
+ float unmodifiedEndLocation = childState.yTranslation + childState.height;
if (mIsExpanded) {
- // Ensure that the heads up is always visible even when scrolled off from the bottom
- float bottomPosition = ambientState.getMaxHeadsUpTranslation() - childState.height;
- childState.yTranslation = Math.min(childState.yTranslation,
- bottomPosition);
+ // Ensure that the heads up is always visible even when scrolled off
+ clampHunToTop(ambientState, row, childState);
+ clampHunToMaxTranslation(ambientState, row, childState);
}
if (row.isPinned()) {
childState.yTranslation = Math.max(childState.yTranslation, 0);
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
- if (!isTopEntry) {
+ StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+ if (!isTopEntry && (!mIsExpanded
+ || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
// Ensure that a headsUp doesn't vertically extend further than the heads-up at
// the top most z-position
- StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
childState.height = row.getIntrinsicHeight();
childState.yTranslation = topState.yTranslation + topState.height
- childState.height;
@@ -532,17 +468,23 @@
}
}
- /**
- * Clamp the yTranslation both up and down to valid positions.
- *
- * @param childViewState the view state of the child
- * @param minHeight the minimum height of this child
- */
- private void clampYTranslation(StackViewState childViewState, int minHeight,
- AmbientState ambientState) {
- clampPositionToBottomStackStart(childViewState, childViewState.height, minHeight,
- ambientState);
- clampPositionToTopStackEnd(childViewState, childViewState.height);
+ private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
+ StackViewState childState) {
+ float newTranslation = Math.max(ambientState.getTopPadding()
+ + ambientState.getStackTranslation(), childState.yTranslation);
+ childState.height = (int) Math.max(childState.height - (newTranslation
+ - childState.yTranslation), row.getMinHeight());
+ childState.yTranslation = newTranslation;
+ }
+
+ private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
+ StackViewState childState) {
+ float newTranslation;
+ float bottomPosition = ambientState.getMaxHeadsUpTranslation() - row.getMinHeight();
+ newTranslation = Math.min(childState.yTranslation, bottomPosition);
+ childState.height = (int) Math.max(childState.height
+ - (childState.yTranslation - newTranslation), row.getMinHeight());
+ childState.yTranslation = newTranslation;
}
/**
@@ -569,19 +511,6 @@
}
}
- /**
- * Clamp the yTranslation of the child up such that its end is at lest on the end of the top
- * stack.
- *
- * @param childViewState the view state of the child
- * @param childHeight the height of this child
- */
- private void clampPositionToTopStackEnd(StackViewState childViewState,
- int childHeight) {
- childViewState.yTranslation = Math.max(childViewState.yTranslation,
- mFirstChildMinHeight - childHeight);
- }
-
private int getMaxAllowedChildHeight(View child) {
if (child instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) child;
@@ -611,9 +540,6 @@
}
childViewState.yTranslation = transitioningPositionStart + offset - newHeight
- getPaddingAfterChild(algorithmState, child);
-
- // We want at least to be at the end of the top stack when collapsing
- clampPositionToTopStackEnd(childViewState, newHeight);
childViewState.location = StackViewState.LOCATION_MAIN_AREA;
}
@@ -642,177 +568,59 @@
}
childViewState.height = minHeight;
childViewState.yTranslation = currentYPosition - minHeight;
- clampPositionToTopStackEnd(childViewState, minHeight);
}
- private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
- ExpandableView child, int numberOfElementsCompletelyIn, int i, int childHeight,
- StackViewState childViewState, float scrollOffset) {
-
-
- // First we calculate the index relative to the current stack window of size at most
- // {@link #MAX_ITEMS_IN_TOP_STACK}
- int paddedIndex = i - 1
- - Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0);
- if (paddedIndex >= 0) {
-
- // We are currently visually entering the top stack
- float distanceToStack = (childHeight + getPaddingAfterChild(algorithmState, child))
- - algorithmState.scrolledPixelsTop;
- if (i == algorithmState.lastTopStackIndex
- && distanceToStack > (mTopStackTotalSize
- + getPaddingAfterChild(algorithmState, child))) {
-
- // Child is currently translating into stack but not yet inside slow down zone.
- // Handle it like the regular scrollview.
- childViewState.yTranslation = scrollOffset;
- } else {
- // Apply stacking logic.
- float numItemsBefore;
- if (i == algorithmState.lastTopStackIndex) {
- numItemsBefore = 1.0f
- - (distanceToStack / (mTopStackTotalSize
- + getPaddingAfterChild(algorithmState, child)));
- } else {
- numItemsBefore = algorithmState.itemsInTopStack - i;
- }
- // The end position of the current child
- float currentChildEndY = mFirstChildMinHeight + mTopStackTotalSize
- - mTopStackIndentationFunctor.getValue(numItemsBefore);
- childViewState.yTranslation = currentChildEndY - childHeight;
- }
- childViewState.location = StackViewState.LOCATION_TOP_STACK_PEEKING;
- } else {
- if (paddedIndex == -1) {
- childViewState.shadowAlpha = 1.0f - algorithmState.partialInTop;
- } else {
- // We are hidden behind the top card and faded out, so we can hide ourselves.
- childViewState.hidden = true;
- childViewState.shadowAlpha = 0.0f;
- }
- childViewState.yTranslation = mFirstChildMinHeight - childHeight;
- childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN;
- }
-
-
- }
/**
- * Find the number of items in the top stack and update the result state if needed.
+ * Update the height of the first child i.e clamp it to the bottom stack
*
- * @param resultState The result state to update if a height change of an child occurs
- * @param algorithmState The state in which the current pass of the algorithm is currently in
+ *
+
+ * @param child the child to update
+ * @param childViewState the viewstate of the child
+ * @param childHeight the height of the child
+ * @param ambientState The ambient state of the algorithm
*/
- private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+ private void updateFirstChildHeight(ExpandableView child, StackViewState childViewState,
+ int childHeight, AmbientState ambientState) {
- // The y Position if the element would be in a regular scrollView
- float yPositionInScrollView = 0.0f;
- int childCount = algorithmState.visibleChildren.size();
- // find the number of elements in the top stack.
- for (int i = 0; i < childCount; i++) {
- ExpandableView child = algorithmState.visibleChildren.get(i);
- StackViewState childViewState = resultState.getViewStateForView(child);
- int childHeight = getMaxAllowedChildHeight(child);
- int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
- float yPositionInScrollViewAfterElement = yPositionInScrollView
- + childHeight
- + paddingAfterChild;
- if (yPositionInScrollView < algorithmState.scrollY) {
- if (i == 0 && algorithmState.scrollY <= mFirstChildMinHeight) {
-
- // The starting position of the bottom stack peek
- int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
- mCollapseSecondCardPadding;
- // Collapse and expand the first child while the shade is being expanded
- float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
- ? mFirstChildMaxHeight
- : childHeight;
- childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
- mFirstChildMinHeight);
- algorithmState.itemsInTopStack = 1.0f;
-
- } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
- // According to the regular scroll view we are fully off screen
- algorithmState.itemsInTopStack += 1.0f;
- if (i == 0) {
- childViewState.height = child.getMinHeight();
- }
- } else {
- // According to the regular scroll view we are partially off screen
-
- // How much did we scroll into this child
- algorithmState.scrolledPixelsTop = algorithmState.scrollY
- - yPositionInScrollView;
- algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight
- + paddingAfterChild);
-
- // Our element can be expanded, so this can get negative
- algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
- algorithmState.itemsInTopStack += algorithmState.partialInTop;
-
- if (i == 0) {
- // If it is expanded we have to collapse it to a new size
- float newSize = yPositionInScrollViewAfterElement
- - paddingAfterChild
- - algorithmState.scrollY + mFirstChildMinHeight;
- newSize = Math.max(mFirstChildMinHeight, newSize);
- algorithmState.itemsInTopStack = 1.0f;
- childViewState.height = (int) newSize;
- }
- algorithmState.lastTopStackIndex = i;
- break;
- }
- } else {
- algorithmState.lastTopStackIndex = i - 1;
- // We are already past the stack so we can end the loop
- break;
- }
- yPositionInScrollView = yPositionInScrollViewAfterElement;
- }
+ // The starting position of the bottom stack peek
+ int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
+ mCollapseSecondCardPadding + ambientState.getScrollY();
+ // Collapse and expand the first child while the shade is being expanded
+ float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
+ ? mFirstChildMaxHeight
+ : childHeight;
+ childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
+ child.getMinHeight());
}
/**
* Calculate the Z positions for all children based on the number of items in both stacks and
* save it in the resultState
- *
- * @param resultState The result state to update the zTranslation values
+ * @param resultState The result state to update the zTranslation values
* @param algorithmState The state in which the current pass of the algorithm is currently in
+ * @param ambientState The ambient state of the algorithm
*/
private void updateZValuesForState(StackScrollState resultState,
- StackScrollAlgorithmState algorithmState) {
+ StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
- for (int i = 0; i < childCount; i++) {
- View child = algorithmState.visibleChildren.get(i);
+ int childrenOnTop = 0;
+ for (int i = childCount - 1; i >= 0; i--) {
+ ExpandableView child = algorithmState.visibleChildren.get(i);
StackViewState childViewState = resultState.getViewStateForView(child);
- if (i < algorithmState.itemsInTopStack) {
- float stackIndex = algorithmState.itemsInTopStack - i;
-
- // Ensure that the topmost item is a little bit higher than the rest when fully
- // scrolled, to avoid drawing errors when swiping it out
- float max = MAX_ITEMS_IN_TOP_STACK + (i == 0 ? 2.5f : 2);
- stackIndex = Math.min(stackIndex, max);
- if (i == 0 && algorithmState.itemsInTopStack < 2.0f) {
-
- // We only have the top item and an additional item in the top stack,
- // Interpolate the index from 0 to 2 while the second item is
- // translating in.
- stackIndex -= 1.0f;
- if (algorithmState.scrollY > mFirstChildMinHeight) {
-
- // Since there is a shadow treshhold, we cant just interpolate from 0 to
- // 2 but we interpolate from 0.1f to 2.0f when scrolled in. The jump in
- // height will not be noticable since we have padding in between.
- stackIndex = 0.1f + stackIndex * 1.9f;
- }
- }
- childViewState.zTranslation = mZBasicHeight
- + stackIndex * mZDistanceBetweenElements;
- } else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
+ if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
+ // We are in the bottom stack
float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack);
- float translationZ = mZBasicHeight
+ childViewState.zTranslation = mZBasicHeight
- numItemsAbove * mZDistanceBetweenElements;
- childViewState.zTranslation = translationZ;
+ } else if (child.mustStayOnScreen()
+ && childViewState.yTranslation < ambientState.getTopPadding()
+ + ambientState.getStackTranslation()) {
+ // TODO; do this more cleanly
+ childrenOnTop++;
+ childViewState.zTranslation = mZBasicHeight
+ + childrenOnTop * mZDistanceBetweenElements;
} else {
childViewState.zTranslation = mZBasicHeight;
}
@@ -897,7 +705,6 @@
}
public void notifyChildrenChanged(final NotificationStackScrollLayout hostView) {
- mFirstChild = hostView.getFirstChildNotGone();
if (mIsExpansionChanging) {
hostView.post(new Runnable() {
@Override
@@ -922,26 +729,6 @@
public int scrollY;
/**
- * The quantity of items which are in the top stack.
- */
- public float itemsInTopStack;
-
- /**
- * how far in is the element currently transitioning into the top stack
- */
- public float partialInTop;
-
- /**
- * The number of pixels the last child in the top stack has scrolled in to the stack
- */
- public float scrolledPixelsTop;
-
- /**
- * The last item index which is in the top stack.
- */
- public int lastTopStackIndex;
-
- /**
* The quantity of items which are in the bottom stack.
*/
public float itemsInBottomStack;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
index 05fa27d..fa15195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
@@ -24,12 +24,11 @@
// These are flags such that we can create masks for filtering.
public static final int LOCATION_UNKNOWN = 0x00;
- public static final int LOCATION_FIRST_CARD = 0x01;
- public static final int LOCATION_TOP_STACK_HIDDEN = 0x02;
- public static final int LOCATION_TOP_STACK_PEEKING = 0x04;
- public static final int LOCATION_MAIN_AREA = 0x08;
- public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x10;
- public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x20;
+ public static final int LOCATION_FIRST_HUN = 0x01;
+ public static final int LOCATION_HIDDEN_TOP = 0x02;
+ public static final int LOCATION_MAIN_AREA = 0x04;
+ public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x08;
+ public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x10;
/** The view isn't layouted at all. */
public static final int LOCATION_GONE = 0x40;
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 3327ec4..3f3f851 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -318,10 +318,10 @@
OVERVIEW_HISTORY = 275;
// Logged when the user pages through overview.
- ACTION_OVERVIEW_PAGE = 276;
+ OVERVIEW_PAGE = 276;
// Logged when the user launches a task from overview.
- ACTION_OVERVIEW_SELECT = 277;
+ OVERVIEW_SELECT = 277;
// Logged when the user views the emergency info.
ACTION_VIEW_EMERGENCY_INFO = 278;
@@ -353,5 +353,21 @@
// Logged when the user undocks a previously docked window by long pressing recents while in
// docked mode.
ACTION_WINDOW_UNDOCK_LONGPRESS = 286;
+
+ // Logged when the user scrolls through overview manually
+ OVERVIEW_SCROLL = 287;
+
+ // Logged when the overview times out automatically selecting an app
+ OVERVIEW_SELECT_TIMEOUT = 288;
+
+ // Logged when a user dismisses a task in overview
+ OVERVIEW_DISMISS = 289;
+
+ // Logged when the user modifying the notification importance slider.
+ ACTION_MODIFY_IMPORTANCE_SLIDER = 290;
+
+ // Logged when the user saves a modification to notification importance. Negative numbers
+ // indicate the user lowered the importance; positive means they increased it.
+ ACTION_SAVE_IMPORTANCE = 291;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 232c080..3335315 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -200,10 +200,6 @@
}
}
- public MotionEventInjector getMotionEventInjector() {
- return mMotionEventInjector;
- }
-
/**
* Gets current event stream state associated with an input event.
* @return The event stream state that should be used for the event. Null if the event should
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 53504cc..4be6833 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -52,7 +52,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -129,6 +128,10 @@
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
+ // TODO: Restructure service initialization so services aren't connected before all of
+ // their capabilities are ready.
+ private static final int WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS = 1000;
+
private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE =
"registerUiTestAutomationService";
@@ -650,7 +653,7 @@
userState.mUiAutomationFlags = flags;
userState.mIsAccessibilityEnabled = true;
userState.mInstalledServices.add(accessibilityServiceInfo);
- if (userState.isUiAutomationSuppressingOtherServices()) {
+ if ((flags & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0) {
// Set the temporary state.
userState.mIsTouchExplorationEnabled = false;
userState.mIsEnhancedWebAccessibilityEnabled = false;
@@ -794,6 +797,8 @@
void setMotionEventInjector(MotionEventInjector motionEventInjector) {
synchronized (mLock) {
mMotionEventInjector = motionEventInjector;
+ // We may be waiting on this object being set
+ mLock.notifyAll();
}
}
@@ -2655,10 +2660,24 @@
@Override
public void sendMotionEvents(int sequence, ParceledListSlice events) {
synchronized (mLock) {
- if (mSecurityPolicy.canPerformGestures(this) && (mMotionEventInjector != null)) {
- mMotionEventInjector.injectEvents((List<MotionEvent>) events.getList(),
- mServiceInterface, sequence);
- return;
+ if (mSecurityPolicy.canPerformGestures(this)) {
+ final long endMillis =
+ SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
+ while ((mMotionEventInjector == null)
+ && (SystemClock.uptimeMillis() < endMillis)) {
+ try {
+ mLock.wait(endMillis - SystemClock.uptimeMillis());
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ if (mMotionEventInjector != null) {
+ mMotionEventInjector.injectEvents((List<MotionEvent>) events.getList(),
+ mServiceInterface, sequence);
+ return;
+ } else {
+ Slog.e(LOG_TAG, "MotionEventInjector installation timed out");
+ }
}
}
try {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index ecba0a4..4dbb490 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -62,6 +62,10 @@
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LockSettingsStorage.CredentialHash;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
import java.util.Arrays;
import java.util.List;
@@ -510,9 +514,9 @@
}
}
- private void unlockUser(int userId, byte[] token) {
+ private void unlockUser(int userId, byte[] token, byte[] secret) {
try {
- ActivityManagerNative.getDefault().unlockUser(userId, token);
+ ActivityManagerNative.getDefault().unlockUser(userId, token, secret);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -560,6 +564,7 @@
getGateKeeperService().clearSecureUserId(userId);
mStorage.writePatternHash(null, userId);
setKeystorePassword(null, userId);
+ clearUserKeyProtection(userId);
return;
}
@@ -573,6 +578,7 @@
byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
if (enrolledHandle != null) {
mStorage.writePatternHash(enrolledHandle, userId);
+ setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
} else {
throw new RemoteException("Failed to enroll pattern");
}
@@ -588,6 +594,7 @@
getGateKeeperService().clearSecureUserId(userId);
mStorage.writePasswordHash(null, userId);
setKeystorePassword(null, userId);
+ clearUserKeyProtection(userId);
return;
}
@@ -601,6 +608,7 @@
byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
if (enrolledHandle != null) {
mStorage.writePasswordHash(enrolledHandle, userId);
+ setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
} else {
throw new RemoteException("Failed to enroll password");
}
@@ -633,6 +641,48 @@
return hash;
}
+ private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr)
+ throws RemoteException {
+ if (vcr == null) {
+ throw new RemoteException("Null response verifying a credential we just set");
+ }
+ if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ throw new RemoteException("Non-OK response verifying a credential we just set: "
+ + vcr.getResponseCode());
+ }
+ byte[] token = vcr.getPayload();
+ if (token == null) {
+ throw new RemoteException("Empty payload verifying a credential we just set");
+ }
+ changeUserKey(userId, token, secretFromCredential(credential));
+ }
+
+ private void clearUserKeyProtection(int userId) throws RemoteException {
+ changeUserKey(userId, null, null);
+ }
+
+ private static byte[] secretFromCredential(String credential) throws RemoteException {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ // Personalize the hash
+ byte[] personalization = "Android FBE credential hash"
+ .getBytes(StandardCharsets.UTF_8);
+ // Pad it to the block size of the hash function
+ personalization = Arrays.copyOf(personalization, 128);
+ digest.update(personalization);
+ digest.update(credential.getBytes(StandardCharsets.UTF_8));
+ return digest.digest();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
+ }
+ }
+
+ private void changeUserKey(int userId, byte[] token, byte[] secret)
+ throws RemoteException {
+ final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
+ getMountService().changeUserKey(userId, userInfo.serialNumber, token, null, secret);
+ }
+
@Override
public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException {
return doVerifyPattern(pattern, false, 0, userId);
@@ -742,11 +792,11 @@
if (Arrays.equals(hash, storedHash.hash)) {
unlockKeystore(credentialUtil.adjustForKeystore(credential), userId);
- // TODO: pass through a meaningful token from gatekeeper to
- // unlock credential keys; for now pass through a stub value to
- // indicate that we came from a user challenge.
- final byte[] token = String.valueOf(userId).getBytes();
- unlockUser(userId, token);
+ // Users with legacy credentials don't have credential-backed
+ // FBE keys, so just pass through a fake token/secret
+ Slog.i(TAG, "Unlocking user with fake token: " + userId);
+ final byte[] fakeToken = String.valueOf(userId).getBytes();
+ unlockUser(userId, fakeToken, fakeToken);
// migrate credential to GateKeeper
credentialUtil.setCredential(credential, null, userId);
@@ -786,11 +836,9 @@
// credential has matched
unlockKeystore(credential, userId);
- // TODO: pass through a meaningful token from gatekeeper to
- // unlock credential keys; for now pass through a stub value to
- // indicate that we came from a user challenge.
- final byte[] token = String.valueOf(userId).getBytes();
- unlockUser(userId, token);
+ Slog.i(TAG, "Unlocking user " + userId +
+ " with token length " + response.getPayload().length);
+ unlockUser(userId, response.getPayload(), secretFromCredential(credential));
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 5120e1b..cbd477a 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2742,8 +2742,30 @@
}
}
+ private SensitiveArg encodeBytes(byte[] bytes) {
+ if (ArrayUtils.isEmpty(bytes)) {
+ return new SensitiveArg("!");
+ } else {
+ return new SensitiveArg(HexDump.toHexString(bytes));
+ }
+ }
+
@Override
- public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+ public void changeUserKey(int userId, int serialNumber,
+ byte[] token, byte[] oldSecret, byte[] newSecret) {
+ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+ waitForReady();
+
+ try {
+ mCryptConnector.execute("cryptfs", "change_user_key", userId, serialNumber,
+ encodeBytes(token), encodeBytes(oldSecret), encodeBytes(newSecret));
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
@@ -2753,16 +2775,9 @@
throw new IllegalStateException("Token required to unlock secure user " + userId);
}
- final String encodedToken;
- if (ArrayUtils.isEmpty(token)) {
- encodedToken = "!";
- } else {
- encodedToken = HexDump.toHexString(token);
- }
-
try {
mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber,
- new SensitiveArg(encodedToken));
+ encodeBytes(token), encodeBytes(secret));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 6062137..9a773f2 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -32,13 +33,16 @@
import android.os.IBinder;
import android.os.UserHandle;
import android.util.Log;
+import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
/**
* Find the best Service, and bind to it.
@@ -64,17 +68,21 @@
private final Runnable mNewServiceWork;
private final Handler mHandler;
- private Object mLock = new Object();
+ private final Object mLock = new Object();
- // all fields below synchronized on mLock
- private IBinder mBinder; // connected service
- private String mPackageName; // current best package
- private int mVersion = Integer.MIN_VALUE; // current best version
- /**
- * Whether the currently-connected service is multiuser-aware. This can change at run-time
- * when switching from one version of a service to another.
- */
- private boolean mIsMultiuser = false;
+ @GuardedBy("mLock")
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
+
+ @GuardedBy("mLock")
+ private IBinder mBoundService;
+ @GuardedBy("mLock")
+ private ComponentName mBoundComponent;
+ @GuardedBy("mLock")
+ private String mBoundPackageName;
+ @GuardedBy("mLock")
+ private int mBoundVersion = Integer.MIN_VALUE;
+ @GuardedBy("mLock")
+ private int mBoundUserId = UserHandle.USER_NULL;
public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
List<String> initialPackageNames) {
@@ -84,7 +92,9 @@
String pkg = initialPackageNames.get(i);
try {
HashSet<Signature> set = new HashSet<Signature>();
- Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
+ Signature[] sigs = pm.getPackageInfo(pkg,
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.GET_SIGNATURES).signatures;
set.addAll(Arrays.asList(sigs));
sigSets.add(set);
} catch (NameNotFoundException e) {
@@ -108,7 +118,7 @@
// Whether to enable service overlay.
boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
- ArrayList<String> initialPackageNames = new ArrayList<String>();
+ ArrayList<String> initialPackageNames = new ArrayList<String>();
if (enableOverlay) {
// A list of package names used to create the signatures.
String[] pkgs = resources.getStringArray(initialPackageNamesResId);
@@ -126,20 +136,32 @@
mSignatureSets = getSignatureSets(context, initialPackageNames);
}
+ /**
+ * Start this watcher, including binding to the current best match and
+ * re-binding to any better matches down the road.
+ * <p>
+ * Note that if there are no matching encryption-aware services, we may not
+ * bind to a real service until after the current user is unlocked.
+ */
public boolean start() {
synchronized (mLock) {
- if (!bindBestPackageLocked(mServicePackageName)) return false;
+ bindBestPackageLocked(mServicePackageName, false);
}
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL);
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- switchUser();
+ switchUser(userId);
+ } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+ unlockUser(userId);
}
}
}, UserHandle.ALL, intentFilter, null, mHandler);
@@ -153,30 +175,36 @@
}
/**
- * Searches and binds to the best package, or do nothing
- * if the best package is already bound.
- * Only checks the named package, or checks all packages if it
- * is null.
- * Return true if a new package was found to bind to.
+ * Searches and binds to the best package, or do nothing if the best package
+ * is already bound, unless force rebinding is requested.
+ *
+ * @param justCheckThisPackage Only consider this package, or consider all
+ * packages if it is {@code null}.
+ * @param forceRebind Force a rebinding to the best package if it's already
+ * bound.
+ * @return {@code true} if a valid package was found to bind to.
*/
- private boolean bindBestPackageLocked(String justCheckThisPackage) {
+ private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
Intent intent = new Intent(mAction);
if (justCheckThisPackage != null) {
intent.setPackage(justCheckThisPackage);
}
- List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
- PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM);
+ final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
+ PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ mCurrentUserId);
int bestVersion = Integer.MIN_VALUE;
- String bestPackage = null;
+ ComponentName bestComponent = null;
boolean bestIsMultiuser = false;
if (rInfos != null) {
for (ResolveInfo rInfo : rInfos) {
- String packageName = rInfo.serviceInfo.packageName;
+ final ComponentName component = rInfo.serviceInfo.getComponentName();
+ final String packageName = component.getPackageName();
// check signature
try {
PackageInfo pInfo;
- pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
if (!isSignatureMatch(pInfo.signatures)) {
Log.w(mTag, packageName + " resolves service " + mAction
+ ", but has wrong signature, ignoring");
@@ -196,9 +224,9 @@
isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
}
- if (version > mVersion) {
+ if (version > bestVersion) {
bestVersion = version;
- bestPackage = packageName;
+ bestComponent = component;
bestIsMultiuser = isMultiuser;
}
}
@@ -207,42 +235,53 @@
Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
(justCheckThisPackage == null ? ""
: "(" + justCheckThisPackage + ") "), rInfos.size(),
- (bestPackage == null ? "no new best package"
- : "new best package: " + bestPackage)));
+ (bestComponent == null ? "no new best component"
+ : "new best component: " + bestComponent)));
}
} else {
if (D) Log.d(mTag, "Unable to query intent services for action: " + mAction);
}
- if (bestPackage != null) {
- bindToPackageLocked(bestPackage, bestVersion, bestIsMultiuser);
- return true;
+
+ if (bestComponent == null) {
+ Slog.w(mTag, "Odd, no component found for service " + mAction);
+ unbindLocked();
+ return false;
}
- return false;
+
+ final int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
+ final boolean alreadyBound = Objects.equals(bestComponent, mBoundComponent)
+ && bestVersion == mBoundVersion && userId == mBoundUserId;
+ if (forceRebind || !alreadyBound) {
+ unbindLocked();
+ bindToPackageLocked(bestComponent, bestVersion, userId);
+ }
+ return true;
}
private void unbindLocked() {
- String pkg;
- pkg = mPackageName;
- mPackageName = null;
- mVersion = Integer.MIN_VALUE;
- mIsMultiuser = false;
- if (pkg != null) {
- if (D) Log.d(mTag, "unbinding " + pkg);
+ ComponentName component;
+ component = mBoundComponent;
+ mBoundComponent = null;
+ mBoundPackageName = null;
+ mBoundVersion = Integer.MIN_VALUE;
+ mBoundUserId = UserHandle.USER_NULL;
+ if (component != null) {
+ if (D) Log.d(mTag, "unbinding " + component);
mContext.unbindService(this);
}
}
- private void bindToPackageLocked(String packageName, int version, boolean isMultiuser) {
- unbindLocked();
+ private void bindToPackageLocked(ComponentName component, int version, int userId) {
Intent intent = new Intent(mAction);
- intent.setPackage(packageName);
- mPackageName = packageName;
- mVersion = version;
- mIsMultiuser = isMultiuser;
- if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ") ("
- + (isMultiuser ? "multi" : "single") + "-user)");
- mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_NOT_VISIBLE, mIsMultiuser ? UserHandle.SYSTEM : UserHandle.CURRENT);
+ intent.setComponent(component);
+ mBoundComponent = component;
+ mBoundPackageName = component.getPackageName();
+ mBoundVersion = version;
+ mBoundUserId = userId;
+ if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
+ mContext.bindServiceAsUser(intent, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
+ new UserHandle(userId));
}
public static boolean isSignatureMatch(Signature[] signatures,
@@ -275,106 +314,92 @@
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
synchronized (mLock) {
- if (packageName.equals(mPackageName)) {
- // package updated, make sure to rebind
- unbindLocked();
- }
- // Need to check all packages because this method is also called when a
- // system app is uninstalled and the stock version in reinstalled.
- bindBestPackageLocked(null);
+ final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
+ bindBestPackageLocked(null, forceRebind);
}
}
@Override
public void onPackageAdded(String packageName, int uid) {
synchronized (mLock) {
- if (packageName.equals(mPackageName)) {
- // package updated, make sure to rebind
- unbindLocked();
- }
- // check the new package is case it is better
- bindBestPackageLocked(null);
+ final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
+ bindBestPackageLocked(null, forceRebind);
}
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
- if (packageName.equals(mPackageName)) {
- unbindLocked();
- // the currently bound package was removed,
- // need to search for a new package
- bindBestPackageLocked(null);
- }
+ final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
+ bindBestPackageLocked(null, forceRebind);
}
}
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
synchronized (mLock) {
- if (packageName.equals(mPackageName)) {
- // service enabled or disabled, make sure to rebind
- unbindLocked();
- }
- // the service might be disabled, need to search for a new
- // package
- bindBestPackageLocked(null);
+ final boolean forceRebind = Objects.equals(packageName, mBoundPackageName);
+ bindBestPackageLocked(null, forceRebind);
}
return super.onPackageChanged(packageName, uid, components);
}
};
@Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
+ public void onServiceConnected(ComponentName component, IBinder binder) {
synchronized (mLock) {
- String packageName = name.getPackageName();
- if (packageName.equals(mPackageName)) {
- if (D) Log.d(mTag, packageName + " connected");
- mBinder = binder;
+ if (component.equals(mBoundComponent)) {
+ if (D) Log.d(mTag, component + " connected");
+ mBoundService = binder;
if (mHandler !=null && mNewServiceWork != null) {
mHandler.post(mNewServiceWork);
}
} else {
- Log.w(mTag, "unexpected onServiceConnected: " + packageName);
+ Log.w(mTag, "unexpected onServiceConnected: " + component);
}
}
}
@Override
- public void onServiceDisconnected(ComponentName name) {
+ public void onServiceDisconnected(ComponentName component) {
synchronized (mLock) {
- String packageName = name.getPackageName();
- if (D) Log.d(mTag, packageName + " disconnected");
+ if (D) Log.d(mTag, component + " disconnected");
- if (packageName.equals(mPackageName)) {
- mBinder = null;
+ if (component.equals(mBoundComponent)) {
+ mBoundService = null;
}
}
}
- public String getBestPackageName() {
+ public @Nullable String getBestPackageName() {
synchronized (mLock) {
- return mPackageName;
+ return mBoundPackageName;
}
}
public int getBestVersion() {
synchronized (mLock) {
- return mVersion;
+ return mBoundVersion;
}
}
- public IBinder getBinder() {
+ public @Nullable IBinder getBinder() {
synchronized (mLock) {
- return mBinder;
+ return mBoundService;
}
}
- public void switchUser() {
+ public void switchUser(int userId) {
synchronized (mLock) {
- if (!mIsMultiuser) {
- unbindLocked();
- bindBestPackageLocked(mServicePackageName);
+ mCurrentUserId = userId;
+ bindBestPackageLocked(mServicePackageName, false);
+ }
+ }
+
+ public void unlockUser(int userId) {
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ bindBestPackageLocked(mServicePackageName, false);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5125133..393edb6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14894,7 +14894,7 @@
pw.println(mi.hasActivities ? ",a" : ",e");
} else {
pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(",");
- pw.println(mi.pss); pw.print(dumpSwapPss ? mi.swapPss : "N/A");
+ pw.print(mi.pss); pw.print(","); pw.println(dumpSwapPss ? mi.swapPss : "N/A");
}
if (mi.subitems != null) {
dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems,
@@ -14959,6 +14959,9 @@
private final void dumpApplicationMemoryUsageHeader(PrintWriter pw, long uptime,
long realtime, boolean isCheckinRequest, boolean isCompact) {
+ if (isCompact) {
+ pw.print("version,"); pw.println(MEMINFO_COMPACT_VERSION);
+ }
if (isCheckinRequest || isCompact) {
// short checkin version
pw.print("time,"); pw.print(uptime); pw.print(","); pw.println(realtime);
@@ -15017,6 +15020,9 @@
return stringifySize(size * 1024, 1024);
}
+ // Update this version number in case you change the 'compact' format
+ private static final int MEMINFO_COMPACT_VERSION = 1;
+
final void dumpApplicationMemoryUsage(FileDescriptor fd,
PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) {
boolean dumpDetails = false;
@@ -20340,8 +20346,8 @@
}
@Override
- public boolean unlockUser(int userId, byte[] token) {
- return mUserController.unlockUser(userId, token);
+ public boolean unlockUser(int userId, byte[] token, byte[] secret) {
+ return mUserController.unlockUser(userId, token, secret);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c352fc8..0bccffa 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1097,6 +1097,9 @@
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
r.stopped = true;
r.state = ActivityState.STOPPED;
+
+ mWindowManager.notifyAppStopped(r.appToken);
+
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
}
@@ -1561,7 +1564,7 @@
resumeNextActivity = false;
}
} else {
- makeVisible(starting, r);
+ makeVisibleIfNeeded(starting, r);
}
// Aggregate current change flags.
configChanges |= r.configChangeFlags;
@@ -1719,28 +1722,30 @@
return behindFullscreenActivity;
}
- private void makeVisible(ActivityRecord starting, ActivityRecord r) {
+ private void makeVisibleIfNeeded(ActivityRecord starting, ActivityRecord r) {
+
// This activity is not currently visible, but is running. Tell it to become visible.
- r.visible = true;
- if (r.state != ActivityState.RESUMED && r != starting) {
- // If this activity is paused, tell it to now show its window.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Making visible and scheduling visibility: " + r);
- try {
- if (mTranslucentActivityWaiting != null) {
- r.updateOptionsLocked(r.returningOptions);
- mUndrawnActivitiesBelowTopTranslucent.add(r);
- }
- setVisible(r, true);
- r.sleeping = false;
- r.app.pendingUiClean = true;
- r.app.thread.scheduleWindowVisibility(r.appToken, true);
- r.stopFreezingScreenLocked(false);
- } catch (Exception e) {
- // Just skip on any failure; we'll make it
- // visible when it next restarts.
- Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
+ if (r.state == ActivityState.RESUMED || r == starting) {
+ return;
+ }
+
+ // If this activity is paused, tell it to now show its window.
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+ "Making visible and scheduling visibility: " + r);
+ try {
+ if (mTranslucentActivityWaiting != null) {
+ r.updateOptionsLocked(r.returningOptions);
+ mUndrawnActivitiesBelowTopTranslucent.add(r);
}
+ setVisible(r, true);
+ r.sleeping = false;
+ r.app.pendingUiClean = true;
+ r.app.thread.scheduleWindowVisibility(r.appToken, true);
+ r.stopFreezingScreenLocked(false);
+ } catch (Exception e) {
+ // Just skip on any failure; we'll make it
+ // visible when it next restarts.
+ Slog.w(TAG, "Exception thrown making visibile: " + r.intent.getComponent(), e);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f53e71a..0beef53 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1932,7 +1932,7 @@
private void ensureConfigurationAndResume(ActivityStack stack, ActivityRecord r,
boolean preserveWindows) {
- if (r == null) {
+ if (r == null || !r.visible) {
return;
}
final boolean updated = stack.ensureActivityConfigurationLocked(r, 0,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2f63b2d3..a355fa4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -783,7 +783,7 @@
return result;
}
- boolean unlockUser(final int userId, byte[] token) {
+ boolean unlockUser(final int userId, byte[] token, byte[] secret) {
if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: unlockUser() from pid="
@@ -796,7 +796,7 @@
final long binderToken = Binder.clearCallingIdentity();
try {
- return unlockUserCleared(userId, token);
+ return unlockUserCleared(userId, token, secret);
} finally {
Binder.restoreCallingIdentity(binderToken);
}
@@ -810,10 +810,10 @@
*/
boolean maybeUnlockUser(final int userId) {
// Try unlocking storage using empty token
- return unlockUserCleared(userId, null);
+ return unlockUserCleared(userId, null, null);
}
- boolean unlockUserCleared(final int userId, byte[] token) {
+ boolean unlockUserCleared(final int userId, byte[] token, byte[] secret) {
synchronized (mService) {
// Bail if already running unlocked
final UserState uss = mStartedUsers.get(userId);
@@ -824,7 +824,7 @@
final UserInfo userInfo = getUserInfo(userId);
final IMountService mountService = getMountService();
try {
- mountService.unlockUserKey(userId, userInfo.serialNumber, token);
+ mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret);
} catch (RemoteException | RuntimeException e) {
Slog.w(TAG, "Failed to unlock: " + e.getMessage());
return false;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7f8099e..b2e6adf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -760,9 +760,7 @@
if (policy == null && meteredHint) {
// policy doesn't exist, and AP is hinting that it's
// metered: create an inferred policy.
- policy = new NetworkPolicy(template, CYCLE_NONE, Time.TIMEZONE_UTC,
- WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER,
- meteredHint, true);
+ policy = newWifiPolicy(template, meteredHint);
addNetworkPolicyLocked(policy);
} else if (policy != null && policy.inferred) {
@@ -778,6 +776,12 @@
}
};
+ static NetworkPolicy newWifiPolicy(NetworkTemplate template, boolean metered) {
+ return new NetworkPolicy(template, CYCLE_NONE, Time.TIMEZONE_UTC,
+ WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER,
+ metered, true);
+ }
+
/**
* Observer that watches for {@link INetworkManagementService} alerts.
*/
@@ -2154,7 +2158,7 @@
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException {
- (new NetworkPolicyManagerShellCommand(this)).exec(
+ (new NetworkPolicyManagerShellCommand(mContext, this)).exec(
this, in, out, err, args, resultReceiver);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 281c3d0..5cd1025 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -16,14 +16,22 @@
package com.android.server.net;
+import static android.net.wifi.WifiInfo.removeDoubleQuotes;
+import static com.android.server.net.NetworkPolicyManagerService.newWifiPolicy;
import static com.android.server.net.NetworkPolicyManagerService.TAG;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import android.content.Context;
import android.net.INetworkPolicyManager;
import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ShellCommand;
@@ -31,10 +39,12 @@
class NetworkPolicyManagerShellCommand extends ShellCommand {
- final INetworkPolicyManager mInterface;
+ private final INetworkPolicyManager mInterface;
+ private final WifiManager mWifiManager;
- NetworkPolicyManagerShellCommand(INetworkPolicyManager service) {
+ NetworkPolicyManagerShellCommand(Context context, INetworkPolicyManager service) {
mInterface = service;
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
}
@Override
@@ -100,7 +110,7 @@
}
switch(type) {
case "metered-network":
- return getNonMobileMeteredNetwork();
+ return getMeteredWifiNetwork();
case "restrict-background":
return getRestrictBackground();
}
@@ -117,7 +127,7 @@
}
switch(type) {
case "metered-network":
- return setNonMobileMeteredNetwork();
+ return setMeteredWifiNetwork();
case "restrict-background":
return setRestrictBackground();
}
@@ -134,9 +144,9 @@
}
switch(type) {
case "metered-networks":
- return listNonMobileMeteredNetworks();
+ return listMeteredWifiNetworks();
case "restrict-background-whitelist":
- return runListRestrictBackgroundWhitelist();
+ return listRestrictBackgroundWhitelist();
}
pw.println("Error: unknown list type '" + type + "'");
return -1;
@@ -172,7 +182,7 @@
return -1;
}
- private int runListRestrictBackgroundWhitelist() throws RemoteException {
+ private int listRestrictBackgroundWhitelist() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final int[] uids = mInterface.getRestrictBackgroundWhitelistedUids();
pw.print("Restrict background whitelisted UIDs: ");
@@ -238,11 +248,11 @@
return 0;
}
- private int listNonMobileMeteredNetworks() throws RemoteException {
+ private int listMeteredWifiNetworks() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String arg = getNextArg();
final Boolean filter = arg == null ? null : Boolean.valueOf(arg);
- for (NetworkPolicy policy : getNonMobilePolicies()) {
+ for (NetworkPolicy policy : getWifiPolicies()) {
if (filter != null && filter.booleanValue() != policy.metered) {
continue;
}
@@ -253,14 +263,14 @@
return 0;
}
- private int getNonMobileMeteredNetwork() throws RemoteException {
+ private int getMeteredWifiNetwork() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String id = getNextArg();
if (id == null) {
pw.println("Error: didn't specify ID");
return -1;
}
- final List<NetworkPolicy> policies = getNonMobilePolicies();
+ final List<NetworkPolicy> policies = getWifiPolicies();
for (NetworkPolicy policy: policies) {
if (id.equals(getNetworkId(policy))) {
pw.println(policy.metered);
@@ -270,7 +280,7 @@
return 0;
}
- private int setNonMobileMeteredNetwork() throws RemoteException {
+ private int setMeteredWifiNetwork() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
final String id = getNextArg();
if (id == null) {
@@ -285,6 +295,7 @@
final boolean metered = Boolean.valueOf(arg);
final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
boolean changed = false;
+ // First try to find a policy with such id
for (NetworkPolicy policy : policies) {
if (policy.template.isMatchRuleMobile() || policy.metered == metered) {
continue;
@@ -298,24 +309,57 @@
}
if (changed) {
mInterface.setNetworkPolicies(policies);
+ return 0;
+ }
+ // Policy not found: check if there is a saved wi-fi with such id.
+ for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
+ final String ssid = removeDoubleQuotes(config.SSID);
+ if (id.equals(ssid)) {
+ final NetworkPolicy policy = newPolicy(ssid);
+ Log.i(TAG, "Creating new policy for " + ssid + ": " + policy);
+ final NetworkPolicy[] newPolicies = new NetworkPolicy[policies.length + 1];
+ System.arraycopy(policies, 0, newPolicies, 0, policies.length);
+ newPolicies[newPolicies.length - 1] = policy;
+ mInterface.setNetworkPolicies(newPolicies);
+ }
}
return 0;
}
- private List<NetworkPolicy> getNonMobilePolicies() throws RemoteException {
+ private List<NetworkPolicy> getWifiPolicies() throws RemoteException {
+ // First gets a list of saved wi-fi networks.
+ final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ final Set<String> ssids = new HashSet<>(configs.size());
+ for (WifiConfiguration config : configs) {
+ ssids.add(removeDoubleQuotes(config.SSID));
+ }
+
+ // Then gets the saved policies.
final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
- final List<NetworkPolicy> nonMobilePolicies = new ArrayList<NetworkPolicy>(policies.length);
+ final List<NetworkPolicy> wifiPolicies = new ArrayList<NetworkPolicy>(policies.length);
for (NetworkPolicy policy: policies) {
if (!policy.template.isMatchRuleMobile()) {
- nonMobilePolicies.add(policy);
+ wifiPolicies.add(policy);
+ final String netId = getNetworkId(policy);
+ ssids.remove(netId);
}
}
- return nonMobilePolicies;
+ // Finally, creates new default policies for saved WI-FIs not policied yet.
+ for (String ssid : ssids) {
+ final NetworkPolicy policy = newPolicy(ssid);
+ wifiPolicies.add(policy);
+ }
+ return wifiPolicies;
+ }
+
+ private NetworkPolicy newPolicy(String ssid) {
+ final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(ssid);
+ final NetworkPolicy policy = newWifiPolicy(template, false);
+ return policy;
}
private String getNetworkId(NetworkPolicy policy) {
- // ids are typically enclosed on double quotes (")
- return policy.template.getNetworkId().replaceAll("^\"|\"$", "");
+ return removeDoubleQuotes(policy.template.getNetworkId());
}
private int getNextBooleanArg() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bf8e8fb..f3e1365 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -39,6 +39,7 @@
import static android.service.notification.NotificationListenerService.TRIM_FULL;
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -2001,6 +2002,9 @@
@Override
public void setImportanceFromAssistant(INotificationListener token, String key,
int importance, CharSequence explanation) throws RemoteException {
+ if (importance == IMPORTANCE_NONE) {
+ throw new IllegalArgumentException("blocking not allowed: key=" + key);
+ }
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 6c338c1..9b5fde0 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -262,6 +262,7 @@
grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(setupPackage, LOCATION_PERMISSIONS, userId);
+ grantRuntimePermissionsLPw(setupPackage, CAMERA_PERMISSIONS, userId);
}
// Camera
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ada7458..04e4a8c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -70,6 +71,7 @@
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -357,6 +359,7 @@
static final int SCAN_REQUIRE_KNOWN = 1<<12;
static final int SCAN_MOVE = 1<<13;
static final int SCAN_INITIAL = 1<<14;
+ static final int SCAN_CHECK_ONLY = 1<<15;
static final int REMOVE_CHATTY = 1<<16;
@@ -1425,6 +1428,7 @@
PackageInstalledInfo res = data.res;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ //TODO: Broadcast for child packages too
final String packageName = res.pkg.applicationInfo.packageName;
res.removedInfo.sendBroadcast(false, true, false);
Bundle extras = new Bundle(1);
@@ -1886,11 +1890,20 @@
}
}
+ void scheduleWritePackageRestrictionsLocked(UserHandle user) {
+ final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+
void scheduleWritePackageRestrictionsLocked(int userId) {
- if (!sUserManager.exists(userId)) return;
- mDirtyUsers.add(userId);
- if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
- mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+ final int[] userIds = (userId == UserHandle.USER_ALL)
+ ? sUserManager.getUserIds() : new int[]{userId};
+ for (int nextUserId : userIds) {
+ if (!sUserManager.exists(nextUserId)) return;
+ mDirtyUsers.add(nextUserId);
+ if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
+ mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
+ }
}
}
@@ -2216,7 +2229,7 @@
+ ps.codePathString + ", installStatus=" + ps.installStatus
+ ", versionCode=" + ps.versionCode + "; scanned versionCode="
+ scannedPkg.mVersionCode);
- removePackageLI(ps, true);
+ removePackageSettingLI(scannedPkg, true);
mExpectingBetter.put(ps.name, ps.codePath);
}
@@ -6263,9 +6276,8 @@
}
}
- private void collectCertificatesLI(PackageParser pp, PackageSetting ps,
- PackageParser.Package pkg, File srcFile, int parseFlags)
- throws PackageManagerException {
+ private void collectCertificatesLI(PackageSetting ps, PackageParser.Package pkg, File srcFile,
+ int parseFlags) throws PackageManagerException {
if (ps != null
&& ps.codePath.equals(srcFile)
&& ps.timeStamp == srcFile.lastModified()
@@ -6294,7 +6306,7 @@
}
try {
- pp.collectCertificates(pkg, parseFlags);
+ PackageParser.collectCertificates(pkg, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
@@ -6338,6 +6350,56 @@
throw PackageManagerException.from(e);
}
+ return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+ }
+
+ /**
+ * Scans a package and returns the newly parsed package.
+ * @throws PackageManagerException on a parse error.
+ */
+ private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
+ int parseFlags, int scanFlags, long currentTime, UserHandle user)
+ throws PackageManagerException {
+ // If the package has children and this is the first dive in the function
+ // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
+ // packages (parent and children) would be successfully scanned before the
+ // actual scan since scanning mutates internal state and we want to atomically
+ // install the package and its children.
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+ if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
+ scanFlags |= SCAN_CHECK_ONLY;
+ }
+ } else {
+ scanFlags &= ~SCAN_CHECK_ONLY;
+ }
+
+ // Scan the parent
+ PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, parseFlags,
+ scanFlags, currentTime, user);
+
+ // Scan the children
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPackage = pkg.childPackages.get(i);
+ scanPackageInternalLI(childPackage, scanFile, parseFlags, scanFlags,
+ currentTime, user);
+ }
+
+
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
+ }
+
+ return scannedPkg;
+ }
+
+ /**
+ * Scans a package and returns the newly parsed package.
+ * @throws PackageManagerException on a parse error.
+ */
+ private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
+ int parseFlags, int scanFlags, long currentTime, UserHandle user)
+ throws PackageManagerException {
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
@@ -6358,7 +6420,39 @@
// package name depending on our state.
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
+
+ // If this is a package we don't know about on the system partition, we
+ // may need to remove disabled child packages on the system partition
+ // or may need to not add child packages if the parent apk is updated
+ // on the data partition and no longer defines this child package.
+ if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
+ // If this is a parent package for an updated system app and this system
+ // app got an OTA update which no longer defines some of the child packages
+ // we have to prune them from the disabled system packages.
+ PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+ if (disabledPs != null) {
+ final int scannedChildCount = (pkg.childPackages != null)
+ ? pkg.childPackages.size() : 0;
+ final int disabledChildCount = disabledPs.childPackageNames != null
+ ? disabledPs.childPackageNames.size() : 0;
+ for (int i = 0; i < disabledChildCount; i++) {
+ String disabledChildPackageName = disabledPs.childPackageNames.get(i);
+ boolean disabledPackageAvailable = false;
+ for (int j = 0; j < scannedChildCount; j++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(j);
+ if (childPkg.packageName.equals(disabledChildPackageName)) {
+ disabledPackageAvailable = true;
+ break;
+ }
+ }
+ if (!disabledPackageAvailable) {
+ mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
+ }
+ }
+ }
+ }
}
+
boolean updatedPkgBetter = false;
// First check if this is a system package that may involve an update
if (updatedPkg != null && (parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
@@ -6391,10 +6485,24 @@
updatedPkg.resourcePathString = scanFile.toString();
}
updatedPkg.pkg = pkg;
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Package " + ps.name + " at " + scanFile
- + " ignored: updated version " + ps.versionCode
- + " better than this " + pkg.mVersionCode);
+ updatedPkg.versionCode = pkg.mVersionCode;
+
+ // Update the disabled system child packages to point to the package too.
+ final int childCount = updatedPkg.childPackageNames != null
+ ? updatedPkg.childPackageNames.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = updatedPkg.childPackageNames.get(i);
+ PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
+ childPackageName);
+ if (updatedChildPkg != null) {
+ updatedChildPkg.pkg = pkg;
+ updatedChildPkg.versionCode = pkg.mVersionCode;
+ }
+ }
+
+ throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
+ + scanFile + " ignored: updated version " + ps.versionCode
+ + " better than this " + pkg.mVersionCode);
} else {
// The current app on the system partition is better than
// what we have updated to on the data partition; switch
@@ -6439,7 +6547,7 @@
}
// Verify certificates against what was last scanned
- collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);
+ collectCertificatesLI(ps, pkg, scanFile, parseFlags);
/*
* A new system app appeared, but we already had a non-system one of the
@@ -6456,7 +6564,7 @@
!= PackageManager.SIGNATURE_MATCH) {
logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
+ " signatures don't match existing userdata copy; removing");
- deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
+ deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false, null);
ps = null;
} else {
/*
@@ -6515,13 +6623,13 @@
}
// Set application objects path explicitly.
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(resourcePath);
- pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+ pkg.setApplicationInfoCodePath(pkg.codePath);
+ pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+ pkg.setApplicationInfoResourcePath(resourcePath);
+ pkg.setApplicationInfoBaseResourcePath(baseResourcePath);
+ pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
@@ -6534,7 +6642,7 @@
*/
if (shouldHideSystemApp) {
synchronized (mPackages) {
- mSettings.disableSystemPackageLPw(pkg.packageName);
+ mSettings.disableSystemPackageLPw(pkg.packageName, true);
}
}
@@ -6932,17 +7040,62 @@
}
private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
+ final PackageParser.Package pkg;
+ synchronized (mPackages) {
+ pkg = mPackages.get(packageName);
+ }
+ if (pkg == null) {
+ Slog.w(TAG, "Failed to delete code cache directory. No package: " + packageName);
+ return;
+ }
+ deleteCodeCacheDirsLI(pkg);
+ }
+
+ private void deleteCodeCacheDirsLI(PackageParser.Package pkg) {
// TODO: triage flags as part of 26466827
final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
- final int[] users = sUserManager.getUserIds();
+ int[] users = sUserManager.getUserIds();
+ int res = 0;
for (int user : users) {
+ // Remove the parent code cache
try {
- mInstaller.clearAppData(volumeUuid, packageName, user,
+ mInstaller.clearAppData(pkg.volumeUuid, pkg.packageName, user,
flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to delete code cache directory", e);
}
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ // Remove the child code cache
+ try {
+ mInstaller.clearAppData(childPkg.volumeUuid, childPkg.packageName,
+ user, flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to delete code cache directory", e);
+ }
+ }
+ }
+ }
+
+ private void setInstallAndUpdateTime(PackageParser.Package pkg, long firstInstallTime,
+ long lastUpdateTime) {
+ // Set parent install/update time
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps != null) {
+ ps.firstInstallTime = firstInstallTime;
+ ps.lastUpdateTime = lastUpdateTime;
+ }
+ // Set children install/update time
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ ps = (PackageSetting) childPkg.mExtras;
+ if (ps != null) {
+ ps.firstInstallTime = firstInstallTime;
+ ps.lastUpdateTime = lastUpdateTime;
+ }
}
}
@@ -7066,11 +7219,39 @@
private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+ // If the package has children and this is the first dive in the function
+ // we recursively scan the package with the SCAN_CHECK_ONLY flag set to see
+ // whether all packages (parent and children) would be successfully scanned
+ // before the actual scan since scanning mutates internal state and we want
+ // to atomically install the package and its children.
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+ if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
+ scanFlags |= SCAN_CHECK_ONLY;
+ }
+ } else {
+ scanFlags &= ~SCAN_CHECK_ONLY;
+ }
+
+ final PackageParser.Package scannedPkg;
try {
- return scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+ // Scan the parent
+ scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+ // Scan the children
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ scanPackageLI(childPkg, parseFlags,
+ scanFlags, currentTime, user);
+ }
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ return scanPackageTracedLI(pkg, parseFlags, scanFlags, currentTime, user);
+ }
+
+ return scannedPkg;
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
@@ -7089,7 +7270,8 @@
}
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+ int scanFlags, long currentTime, UserHandle user)
+ throws PackageManagerException {
final File scanFile = new File(pkg.codePath);
if (pkg.applicationInfo.getCodePath() == null ||
pkg.applicationInfo.getResourcePath() == null) {
@@ -7125,28 +7307,30 @@
"Core android package being redefined. Skipping.");
}
- // Set up information for our fall-back user intent resolution activity.
- mPlatformPackage = pkg;
- pkg.mVersionCode = mSdkVersion;
- mAndroidApplication = pkg.applicationInfo;
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+ // Set up information for our fall-back user intent resolution activity.
+ mPlatformPackage = pkg;
+ pkg.mVersionCode = mSdkVersion;
+ mAndroidApplication = pkg.applicationInfo;
- if (!mResolverReplaced) {
- mResolveActivity.applicationInfo = mAndroidApplication;
- mResolveActivity.name = ResolverActivity.class.getName();
- mResolveActivity.packageName = mAndroidApplication.packageName;
- mResolveActivity.processName = "system:ui";
- mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
- mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
- mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
- mResolveActivity.exported = true;
- mResolveActivity.enabled = true;
- mResolveInfo.activityInfo = mResolveActivity;
- mResolveInfo.priority = 0;
- mResolveInfo.preferredOrder = 0;
- mResolveInfo.match = 0;
- mResolveComponentName = new ComponentName(
- mAndroidApplication.packageName, mResolveActivity.name);
+ if (!mResolverReplaced) {
+ mResolveActivity.applicationInfo = mAndroidApplication;
+ mResolveActivity.name = ResolverActivity.class.getName();
+ mResolveActivity.packageName = mAndroidApplication.packageName;
+ mResolveActivity.processName = "system:ui";
+ mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
+ mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
+ mResolveActivity.exported = true;
+ mResolveActivity.enabled = true;
+ mResolveInfo.activityInfo = mResolveActivity;
+ mResolveInfo.priority = 0;
+ mResolveInfo.preferredOrder = 0;
+ mResolveInfo.match = 0;
+ mResolveComponentName = new ComponentName(
+ mAndroidApplication.packageName, mResolveActivity.name);
+ }
}
}
}
@@ -7156,39 +7340,43 @@
Log.d(TAG, "Scanning package " + pkg.packageName);
}
- if (mPackages.containsKey(pkg.packageName)
- || mSharedLibraries.containsKey(pkg.packageName)) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.packageName
- + " already installed. Skipping duplicate.");
- }
+ synchronized (mPackages) {
+ if (mPackages.containsKey(pkg.packageName)
+ || mSharedLibraries.containsKey(pkg.packageName)) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkg.packageName
+ + " already installed. Skipping duplicate.");
+ }
- // If we're only installing presumed-existing packages, require that the
- // scanned APK is both already known and at the path previously established
- // for it. Previously unknown packages we pick up normally, but if we have an
- // a priori expectation about this package's install presence, enforce it.
- // With a singular exception for new system packages. When an OTA contains
- // a new system package, we allow the codepath to change from a system location
- // to the user-installed location. If we don't allow this change, any newer,
- // user-installed version of the application will be ignored.
- if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mExpectingBetter.containsKey(pkg.packageName)) {
- logCriticalInfo(Log.WARN,
- "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
- } else {
- PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
- if (known != null) {
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Examining " + pkg.codePath
- + " and requiring known paths " + known.codePathString
- + " & " + known.resourcePathString);
- }
- if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
- || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.packageName
- + " found at " + pkg.applicationInfo.getCodePath()
- + " but expected at " + known.codePathString + "; ignoring.");
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mExpectingBetter.containsKey(pkg.packageName)) {
+ logCriticalInfo(Log.WARN,
+ "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkg.packageName);
+ } else {
+ PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.codePath
+ + " and requiring known paths " + known.codePathString
+ + " & " + known.resourcePathString);
+ }
+ if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
+ || !pkg.applicationInfo.getResourcePath().equals(
+ known.resourcePathString)) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.packageName
+ + " found at " + pkg.applicationInfo.getCodePath()
+ + " but expected at " + known.codePathString
+ + "; ignoring.");
+ }
}
}
}
@@ -7208,6 +7396,11 @@
pkg.mAdoptPermissions = null;
}
+ // Getting the package setting may have a side-effect, so if we
+ // are only checking if scan would succeed, stash a copy of the
+ // old setting to restore at the end.
+ PackageSetting nonMutatedPs = null;
+
// writer
synchronized (mPackages) {
if (pkg.mSharedUserId != null) {
@@ -7279,6 +7472,14 @@
+ " was transferred to another, but its .apk remains");
}
+ // See comments in nonMutatedPs declaration
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ PackageSetting foundPs = mSettings.peekPackageLPr(pkg.packageName);
+ if (foundPs != null) {
+ nonMutatedPs = new PackageSetting(foundPs);
+ }
+ }
+
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
@@ -7305,13 +7506,15 @@
reportSettingsProblem(Log.WARN, msg);
// Make a note of it.
- mTransferedPackages.add(origPackage.name);
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
+ mTransferedPackages.add(origPackage.name);
+ }
// No longer need to retain this.
pkgSetting.origPackage = null;
}
- if (realName != null) {
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {
// Make a note of it.
mTransferedPackages.add(pkg.packageName);
}
@@ -7413,7 +7616,7 @@
}
}
- if (pkg.mAdoptPermissions != null) {
+ if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
// This package wants to adopt ownership of permissions from
// another package.
for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
@@ -7537,6 +7740,33 @@
ArrayList<PackageParser.Package> clientLibPkgs = null;
+ if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+ if (nonMutatedPs != null) {
+ synchronized (mPackages) {
+ mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);
+ }
+ }
+ return pkg;
+ }
+
+ // Only privileged apps and updated privileged apps can add child packages.
+ if (pkg.childPackages != null && !pkg.childPackages.isEmpty()) {
+ if ((parseFlags & PARSE_IS_PRIVILEGED) == 0) {
+ throw new PackageManagerException("Only privileged apps and updated "
+ + "privileged apps can add child packages. Ignoring package "
+ + pkg.packageName);
+ }
+ final int childCount = pkg.childPackages.size();
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ if (mSettings.hasOtherDisabledSystemPkgWithChildLPr(pkg.packageName,
+ childPkg.packageName)) {
+ throw new PackageManagerException("Cannot override a child package of "
+ + "another disabled system app. Ignoring package " + pkg.packageName);
+ }
+ }
+ }
+
// writer
synchronized (mPackages) {
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
@@ -7821,7 +8051,7 @@
// Now that permission groups have a special meaning, we ignore permission
// groups for legacy apps to prevent unexpected behavior. In particular,
- // permissions for one app being granted to someone just becuase they happen
+ // permissions for one app being granted to someone just becase they happen
// to be in a group defined by another app (before this had no implications).
if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
p.group = mPermissionGroups.get(p.info.group);
@@ -7990,7 +8220,7 @@
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
- public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
+ private void derivePackageAbi(PackageParser.Package pkg, File scanFile,
String cpuAbiOverride, boolean extractLibs)
throws PackageManagerException {
// TODO: We can probably be smarter about this stuff. For installed apps,
@@ -8071,16 +8301,17 @@
if (abi32 >= 0) {
final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
if (abi64 >= 0) {
- pkg.applicationInfo.secondaryCpuAbi = abi;
+ if (cpuAbiOverride == null && pkg.use32bitAbi) {
+ pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
+ pkg.applicationInfo.primaryCpuAbi = abi;
+ } else {
+ pkg.applicationInfo.secondaryCpuAbi = abi;
+ }
} else {
pkg.applicationInfo.primaryCpuAbi = abi;
}
}
- if (cpuAbiOverride != null &&
- cpuAbiOverride.equals(pkg.applicationInfo.secondaryCpuAbi)) {
- pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
- pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
- }
+
} else {
String[] abiList = (cpuAbiOverride != null) ?
new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
@@ -8466,6 +8697,17 @@
}
}
+ private void killPackage(PackageParser.Package pkg, String reason) {
+ // Kill the parent package
+ killApplication(pkg.packageName, pkg.applicationInfo.uid, reason);
+ // Kill the child packages
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ killApplication(childPkg.packageName, childPkg.applicationInfo.uid, reason);
+ }
+ }
+
private void killApplication(String pkgName, int appId, String reason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
@@ -8479,7 +8721,24 @@
}
}
- void removePackageLI(PackageSetting ps, boolean chatty) {
+ private void removePackageSettingLI(PackageParser.Package pkg, boolean chatty) {
+ // Remove the parent package setting
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps != null) {
+ removePackageSettingLI(ps, chatty);
+ }
+ // Remove the child package setting
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ ps = (PackageSetting) childPkg.mExtras;
+ if (ps != null) {
+ removePackageSettingLI(ps, chatty);
+ }
+ }
+ }
+
+ void removePackageSettingLI(PackageSetting ps, boolean chatty) {
if (DEBUG_INSTALL) {
if (chatty)
Log.d(TAG, "Removing package " + ps.name);
@@ -8503,8 +8762,17 @@
// writer
synchronized (mPackages) {
+ // Remove the parent package
mPackages.remove(pkg.applicationInfo.packageName);
cleanPackageDataStructuresLILPw(pkg, chatty);
+
+ // Remove the child packages
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ mPackages.remove(childPkg.applicationInfo.packageName);
+ cleanPackageDataStructuresLILPw(childPkg, chatty);
+ }
}
}
@@ -8708,6 +8976,17 @@
static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+ private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
+ // Update the parent permissions
+ updatePermissionsLPw(pkg.packageName, pkg, flags);
+ // Update the child permissions
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ updatePermissionsLPw(childPkg.packageName, childPkg, flags);
+ }
+ }
+
private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
int flags) {
final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
@@ -9125,7 +9404,7 @@
if (pkg.isUpdatedSystemApp()) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPs.getPermissionsState().hasInstallPermission(perm)) {
+ if (sysPs != null && sysPs.getPermissionsState().hasInstallPermission(perm)) {
// If the original was granted this permission, we take
// that grant decision as read and propagate it to the
// update.
@@ -9139,16 +9418,38 @@
// before. In this case we do want to allow the app to
// now get the new permission if the ancestral apk is
// privileged to get it.
- if (sysPs.pkg != null && sysPs.isPrivileged()) {
- for (int j=0;
- j<sysPs.pkg.requestedPermissions.size(); j++) {
- if (perm.equals(
- sysPs.pkg.requestedPermissions.get(j))) {
+ if (sysPs != null && sysPs.pkg != null && sysPs.isPrivileged()) {
+ for (int j = 0; j < sysPs.pkg.requestedPermissions.size(); j++) {
+ if (perm.equals(sysPs.pkg.requestedPermissions.get(j))) {
allowed = true;
break;
}
}
}
+ // Also if a privileged parent package on the system image or any of
+ // its children requested a privileged permission, the updated child
+ // packages can also get the permission.
+ if (pkg.parentPackage != null) {
+ final PackageSetting disabledSysParentPs = mSettings
+ .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
+ if (disabledSysParentPs != null && disabledSysParentPs.pkg != null
+ && disabledSysParentPs.isPrivileged()) {
+ if (isPackageRequestingPermission(disabledSysParentPs.pkg, perm)) {
+ allowed = true;
+ } else if (disabledSysParentPs.pkg.childPackages != null) {
+ final int count = disabledSysParentPs.pkg.childPackages.size();
+ for (int i = 0; i < count; i++) {
+ PackageParser.Package disabledSysChildPkg =
+ disabledSysParentPs.pkg.childPackages.get(i);
+ if (isPackageRequestingPermission(disabledSysChildPkg,
+ perm)) {
+ allowed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
}
} else {
allowed = isPrivilegedApp(pkg);
@@ -9192,6 +9493,17 @@
return allowed;
}
+ private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
+ final int permCount = pkg.requestedPermissions.size();
+ for (int j = 0; j < permCount; j++) {
+ String requestedPermission = pkg.requestedPermissions.get(j);
+ if (permission.equals(requestedPermission)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
@@ -11780,20 +12092,20 @@
resourceFile = afterCodeFile;
// Reflect the rename in scanned details
- pkg.codePath = afterCodeFile.getAbsolutePath();
- pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.baseCodePath);
- pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.splitCodePaths);
+ pkg.setCodePath(afterCodeFile.getAbsolutePath());
+ pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.baseCodePath));
+ pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.splitCodePaths));
// Reflect the rename in app info
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(pkg.codePath);
- pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+ pkg.setApplicationInfoCodePath(pkg.codePath);
+ pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+ pkg.setApplicationInfoResourcePath(pkg.codePath);
+ pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
return true;
}
@@ -12035,20 +12347,20 @@
final File afterCodeFile = new File(packagePath);
// Reflect the rename in scanned details
- pkg.codePath = afterCodeFile.getAbsolutePath();
- pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.baseCodePath);
- pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,
- pkg.splitCodePaths);
+ pkg.setCodePath(afterCodeFile.getAbsolutePath());
+ pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.baseCodePath));
+ pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, pkg.splitCodePaths));
// Reflect the rename in app info
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(pkg.codePath);
- pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+ pkg.setApplicationInfoCodePath(pkg.codePath);
+ pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+ pkg.setApplicationInfoResourcePath(pkg.codePath);
+ pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
return true;
}
@@ -12227,13 +12539,13 @@
}
// Reflect the move in app info
- pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
- pkg.applicationInfo.setCodePath(pkg.codePath);
- pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
- pkg.applicationInfo.setResourcePath(pkg.codePath);
- pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
- pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
+ pkg.setApplicationVolumeUuid(pkg.volumeUuid);
+ pkg.setApplicationInfoCodePath(pkg.codePath);
+ pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
+ pkg.setApplicationInfoResourcePath(pkg.codePath);
+ pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
+ pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);
return true;
}
@@ -12427,7 +12739,7 @@
PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
+ updateSettingsLI(newPackage, installerPackageName, null, null, res, user);
prepareAppDataAfterInstall(newPackage);
// delete the partially installed application. the data directory will have to be
@@ -12439,7 +12751,7 @@
// install.
deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
- res.removedInfo, true);
+ res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
@@ -12508,16 +12820,16 @@
if (DEBUG_INSTALL) Slog.d(TAG, "replacePackageLI: new=" + pkg + ", old=" + oldPackage);
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
- if(!checkUpgradeKeySetLP(ps, pkg)) {
+ if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package not signed by keys specified by upgrade-keysets: "
- + pkgName);
+ + pkgName);
return;
}
} else {
// default to original signature matching
if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
- != PackageManager.SIGNATURE_MATCH) {
+ != PackageManager.SIGNATURE_MATCH) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package has a different signature: " + pkgName);
return;
@@ -12535,33 +12847,30 @@
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {
replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+ user, allUsers, perUserInstalled, installerPackageName, res);
} else {
replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanFlags,
- user, allUsers, perUserInstalled, installerPackageName, volumeUuid, res);
+ user, allUsers, perUserInstalled, installerPackageName, res);
}
}
private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
- String volumeUuid, PackageInstalledInfo res) {
- String pkgName = deletedPackage.packageName;
- boolean deletedPkg = true;
- boolean updatedSettings = false;
-
+ PackageInstalledInfo res) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceNonSystemPackageLI: new=" + pkg + ", old="
+ deletedPackage);
- long origUpdateTime;
- if (pkg.mExtras != null) {
- origUpdateTime = ((PackageSetting)pkg.mExtras).lastUpdateTime;
- } else {
- origUpdateTime = 0;
- }
+
+ String pkgName = deletedPackage.packageName;
+ boolean deletedPkg = true;
+ boolean addedPkg = false;
+
+ final long origUpdateTime = (pkg.mExtras != null)
+ ? ((PackageSetting)pkg.mExtras).lastUpdateTime : 0;
// First delete the existing package while retaining the data directory
if (!deletePackageLI(pkgName, null, true, null, null, PackageManager.DELETE_KEEP_DATA,
- res.removedInfo, true)) {
+ res.removedInfo, true, pkg)) {
// If the existing package wasn't successfully deleted
res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, "replaceNonSystemPackageLI");
deletedPkg = false;
@@ -12580,33 +12889,30 @@
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
- deleteCodeCacheDirsLI(pkg.volumeUuid, pkgName);
+ deleteCodeCacheDirsLI(pkg);
+
try {
final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
scanFlags | SCAN_UPDATE_TIME, System.currentTimeMillis(), user);
- updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+ updateSettingsLI(newPackage, installerPackageName, allUsers,
perUserInstalled, res, user);
prepareAppDataAfterInstall(newPackage);
- updatedSettings = true;
+ addedPkg = true;
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
}
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
- // remove package from internal structures. Note that we want deletePackageX to
- // delete the package data and cache directories that it created in
- // scanPackageLocked, unless those directories existed before we even tried to
- // install.
- if(updatedSettings) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
- deletePackageLI(
- pkgName, null, true, allUsers, perUserInstalled,
- PackageManager.DELETE_KEEP_DATA,
- res.removedInfo, true);
+ if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, rolling pack: " + pkgName);
+
+ // Revert all internal state mutations and added folders for the failed install
+ if (addedPkg) {
+ deletePackageLI(pkgName, null, true, allUsers, perUserInstalled,
+ PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
- // Since we failed to install the new package we need to restore the old
- // package that we deleted.
+
+ // Restore the old package
if (deletedPkg) {
if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage);
File restoreFile = new File(deletedPackage.codePath);
@@ -12624,14 +12930,17 @@
+ e.getMessage());
return;
}
- // Restore of old package succeeded. Update permissions.
- // writer
+
synchronized (mPackages) {
- updatePermissionsLPw(deletedPackage.packageName, deletedPackage,
- UPDATE_PERMISSIONS_ALL);
- // can downgrade to reader
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(deletedPackage, installerPackageName);
+
+ // Update permissions for restored package
+ updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+
mSettings.writeLPr();
}
+
Slog.i(TAG, "Successfully restored package : " + pkgName + " after failed upgrade");
}
}
@@ -12640,89 +12949,100 @@
private void replaceSystemPackageLI(PackageParser.Package deletedPackage,
PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user,
int[] allUsers, boolean[] perUserInstalled, String installerPackageName,
- String volumeUuid, PackageInstalledInfo res) {
+ PackageInstalledInfo res) {
if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg
+ ", old=" + deletedPackage);
- boolean disabledSystem = false;
- boolean updatedSettings = false;
+
+ final boolean disabledSystem;
+
+ // Set the system/privileged flags as needed
parseFlags |= PackageParser.PARSE_IS_SYSTEM;
- if ((deletedPackage.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+ if ((deletedPackage.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
!= 0) {
parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
}
- String packageName = deletedPackage.packageName;
- if (packageName == null) {
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "Attempt to delete null packageName.");
- return;
- }
- PackageParser.Package oldPkg;
- PackageSetting oldPkgSetting;
- // reader
- synchronized (mPackages) {
- oldPkg = mPackages.get(packageName);
- oldPkgSetting = mSettings.mPackages.get(packageName);
- if((oldPkg == null) || (oldPkg.applicationInfo == null) ||
- (oldPkgSetting == null)) {
- res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "Couldn't find package " + packageName + " information");
- return;
- }
- }
- killApplication(packageName, oldPkg.applicationInfo.uid, "replace sys pkg");
+ // Kill package processes including services, providers, etc.
+ killPackage(deletedPackage, "replace sys pkg");
- res.removedInfo.uid = oldPkg.applicationInfo.uid;
- res.removedInfo.removedPackage = packageName;
+ // Report the result for the parent package only
+ res.removedInfo.uid = deletedPackage.applicationInfo.uid;
+ res.removedInfo.removedPackage = deletedPackage.packageName;
+
// Remove existing system package
- removePackageLI(oldPkgSetting, true);
- // writer
- synchronized (mPackages) {
- disabledSystem = mSettings.disableSystemPackageLPw(packageName);
- if (!disabledSystem && deletedPackage != null) {
- // We didn't need to disable the .apk as a current system package,
- // which means we are replacing another update that is already
- // installed. We need to make sure to delete the older one's .apk.
- res.removedInfo.args = createInstallArgsForExisting(0,
- deletedPackage.applicationInfo.getCodePath(),
- deletedPackage.applicationInfo.getResourcePath(),
- getAppDexInstructionSets(deletedPackage.applicationInfo));
- } else {
- res.removedInfo.args = null;
- }
+ removePackageSettingLI(deletedPackage, true);
+
+ disabledSystem = disableSystemPackageLPw(deletedPackage, pkg);
+ if (!disabledSystem) {
+ // We didn't need to disable the .apk as a current system package,
+ // which means we are replacing another update that is already
+ // installed. We need to make sure to delete the older one's .apk.
+ res.removedInfo.args = createInstallArgsForExisting(0,
+ deletedPackage.applicationInfo.getCodePath(),
+ deletedPackage.applicationInfo.getResourcePath(),
+ getAppDexInstructionSets(deletedPackage.applicationInfo));
+ } else {
+ res.removedInfo.args = null;
}
// Successfully disabled the old package. Now proceed with re-installation
- deleteCodeCacheDirsLI(pkg.volumeUuid, packageName);
+ deleteCodeCacheDirsLI(pkg);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
- pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
+ ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
PackageParser.Package newPackage = null;
try {
+ // Add the package to the internal data structures
newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags, 0, user);
- if (newPackage.mExtras != null) {
- final PackageSetting newPkgSetting = (PackageSetting) newPackage.mExtras;
- newPkgSetting.firstInstallTime = oldPkgSetting.firstInstallTime;
- newPkgSetting.lastUpdateTime = System.currentTimeMillis();
- // is the update attempting to change shared user? that isn't going to work...
- if (oldPkgSetting.sharedUser != newPkgSetting.sharedUser) {
- res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Forbidding shared user change from " + oldPkgSetting.sharedUser
- + " to " + newPkgSetting.sharedUser);
- updatedSettings = true;
- }
+ // Set the update and install times
+ PackageSetting deletedPkgSetting = (PackageSetting) deletedPackage.mExtras;
+ setInstallAndUpdateTime(newPackage, deletedPkgSetting.firstInstallTime,
+ System.currentTimeMillis());
+
+ // Check for shared user id changes
+ String invalidPackageName = getParentOrChildPackageChangedSharedUser(deletedPackage, newPackage);
+ if (invalidPackageName != null) {
+ res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ "Forbidding shared user change from " + deletedPkgSetting.sharedUser
+ + " to " + invalidPackageName);
}
+ // Update the package dynamic state if succeeded
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSettingsLI(newPackage, installerPackageName, volumeUuid, allUsers,
+ // Now that the install succeeded make sure we remove data
+ // directories for any child package the update removed.
+ final int deletedChildCount = (deletedPackage.childPackages != null)
+ ? deletedPackage.childPackages.size() : 0;
+ final int newChildCount = (newPackage.childPackages != null)
+ ? newPackage.childPackages.size() : 0;
+ for (int i = 0; i < deletedChildCount; i++) {
+ PackageParser.Package deletedChildPkg = deletedPackage.childPackages.get(i);
+ boolean childPackageDeleted = true;
+ for (int j = 0; j < newChildCount; j++) {
+ PackageParser.Package newChildPkg = newPackage.childPackages.get(j);
+ if (deletedChildPkg.packageName.equals(newChildPkg.packageName)) {
+ childPackageDeleted = false;
+ break;
+ }
+ }
+ if (childPackageDeleted) {
+ PackageSetting ps = mSettings.getDisabledSystemPkgLPr(
+ deletedChildPkg.packageName);
+ if (ps != null) {
+ removePackageDataLI(ps, allUsers, perUserInstalled, null, 0, false);
+ }
+ }
+ }
+
+ updateSettingsLI(newPackage, installerPackageName, allUsers,
perUserInstalled, res, user);
prepareAppDataAfterInstall(newPackage);
- updatedSettings = true;
}
-
} catch (PackageManagerException e) {
+ res.returnCode = INSTALL_FAILED_INTERNAL_ERROR;
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
@@ -12734,21 +13054,111 @@
}
// Add back the old system package
try {
- scanPackageTracedLI(oldPkg, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
+ scanPackageTracedLI(deletedPackage, parseFlags, SCAN_UPDATE_SIGNATURE, 0, user);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
}
- // Restore the old system information in Settings
+
synchronized (mPackages) {
if (disabledSystem) {
- mSettings.enableSystemPackageLPw(packageName);
+ enableSystemPackageLPw(deletedPackage);
}
- if (updatedSettings) {
- mSettings.setInstallerPackageName(packageName,
- oldPkgSetting.installerPackageName);
- }
+
+ // Ensure the installer package name up to date
+ setInstallerPackageNameLPw(deletedPackage, installerPackageName);
+
+ // Update permissions for restored package
+ updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
+
mSettings.writeLPr();
}
+
+ Slog.i(TAG, "Successfully restored package : " + deletedPackage.packageName
+ + " after failed upgrade");
+ }
+ }
+
+ /**
+ * Checks whether the parent or any of the child packages have a change shared
+ * user. For a package to be a valid update the shred users of the parent and
+ * the children should match. We may later support changing child shared users.
+ * @param oldPkg The updated package.
+ * @param newPkg The update package.
+ * @return The shared user that change between the versions.
+ */
+ private String getParentOrChildPackageChangedSharedUser(PackageParser.Package oldPkg,
+ PackageParser.Package newPkg) {
+ // Check parent shared user
+ if (!Objects.equals(oldPkg.mSharedUserId, newPkg.mSharedUserId)) {
+ return newPkg.packageName;
+ }
+ // Check child shared users
+ final int oldChildCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
+ final int newChildCount = (newPkg.childPackages != null) ? newPkg.childPackages.size() : 0;
+ for (int i = 0; i < newChildCount; i++) {
+ PackageParser.Package newChildPkg = newPkg.childPackages.get(i);
+ // If this child was present, did it have the same shared user?
+ for (int j = 0; j < oldChildCount; j++) {
+ PackageParser.Package oldChildPkg = oldPkg.childPackages.get(j);
+ if (newChildPkg.packageName.equals(oldChildPkg.packageName)
+ && !Objects.equals(newChildPkg.mSharedUserId, oldChildPkg.mSharedUserId)) {
+ return newChildPkg.packageName;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void removeNativeBinariesLI(PackageParser.Package pkg) {
+ // Remove the lib path for the parent package
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+ }
+ // Remove the lib path for the child packages
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ ps = (PackageSetting) pkg.childPackages.get(i).mExtras;
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+ }
+ }
+ }
+
+ private void enableSystemPackageLPw(PackageParser.Package pkg) {
+ // Enable the parent package
+ mSettings.enableSystemPackageLPw(pkg.packageName);
+ // Enable the child packages
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ mSettings.enableSystemPackageLPw(childPkg.packageName);
+ }
+ }
+
+ private boolean disableSystemPackageLPw(PackageParser.Package oldPkg,
+ PackageParser.Package newPkg) {
+ // Disable the parent package (parent always replaced)
+ boolean disabled = mSettings.disableSystemPackageLPw(oldPkg.packageName, true);
+ // Disable the child packages
+ final int childCount = (oldPkg.childPackages != null) ? oldPkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = oldPkg.childPackages.get(i);
+ final boolean replace = newPkg.hasChildPackage(childPkg.packageName);
+ disabled |= mSettings.disableSystemPackageLPw(childPkg.packageName, replace);
+ }
+ return disabled;
+ }
+
+ private void setInstallerPackageNameLPw(PackageParser.Package pkg,
+ String installerPackageName) {
+ // Enable the parent package
+ mSettings.setInstallerPackageName(pkg.packageName, installerPackageName);
+ // Enable the child packages
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = pkg.childPackages.get(i);
+ mSettings.setInstallerPackageName(childPkg.packageName, installerPackageName);
}
}
@@ -12813,8 +13223,23 @@
}
private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
- String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
- UserHandle user) {
+ int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) {
+ // Update the parent package setting
+ updateSettingsInternalLI(newPackage, installerPackageName, allUsers, perUserInstalled,
+ res, user);
+ // Update the child packages setting
+ final int childCount = (newPackage.childPackages != null)
+ ? newPackage.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPackage = newPackage.childPackages.get(i);
+ updateSettingsInternalLI(childPackage, installerPackageName, allUsers, perUserInstalled,
+ res, user);
+ }
+ }
+
+ private void updateSettingsInternalLI(PackageParser.Package newPackage,
+ String installerPackageName, int[] allUsers, boolean[] perUserInstalled,
+ PackageInstalledInfo res, UserHandle user) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
String pkgName = newPackage.packageName;
@@ -12959,7 +13384,7 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- pp.collectCertificates(pkg, parseFlags);
+ PackageParser.collectCertificates(pkg, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
@@ -12994,8 +13419,17 @@
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
}
- // Prevent apps opting out from runtime permissions
+ // Child packages are installed through the parent package
+ if (pkg.parentPackage != null) {
+ res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ "Package " + pkg.packageName + " is child of package "
+ + pkg.parentPackage.parentPackage + ". Child packages "
+ + "can be updated only through the parent package.");
+ return;
+ }
+
if (replace) {
+ // Prevent apps opting out from runtime permissions
PackageParser.Package oldPackage = mPackages.get(pkgName);
final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
@@ -13007,6 +13441,15 @@
+ " target SDK " + oldTargetSdk + " does.");
return;
}
+
+ // Prevent installing of child packages
+ if (oldPackage.parentPackage != null) {
+ res.setError(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ "Package " + pkg.packageName + " is child of package "
+ + oldPackage.parentPackage + ". Child packages "
+ + "can be updated only through the parent package.");
+ return;
+ }
}
}
@@ -13080,7 +13523,6 @@
}
}
}
-
}
if (systemApp) {
@@ -13526,9 +13968,8 @@
synchronized (mInstallLock) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
- res = deletePackageLI(packageName, removeForUser,
- true, allUsers, perUserInstalled,
- flags | REMOVE_CHATTY, info, true);
+ res = deletePackageLI(packageName, removeForUser, true, allUsers, perUserInstalled,
+ flags | REMOVE_CHATTY, info, true, null);
systemUpdate = info.isRemovedPackageSystemUpdate;
synchronized (mPackages) {
if (res) {
@@ -13617,7 +14058,7 @@
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = ps.name;
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + ps);
- removePackageLI(ps, (flags&REMOVE_CHATTY) != 0);
+ removePackageSettingLI(ps, (flags&REMOVE_CHATTY) != 0);
// Retrieve object to delete permissions for shared user later on
final PackageSetting deletedPs;
// reader
@@ -13713,27 +14154,36 @@
/*
* Tries to delete system package.
*/
- private boolean deleteSystemPackageLI(PackageSetting newPs,
- int[] allUserHandles, boolean[] perUserInstalled,
- int flags, PackageRemovedInfo outInfo, boolean writeSettings) {
+ private boolean deleteSystemPackageLI(PackageParser.Package deletedPkg,
+ PackageSetting deletedPs, int[] allUserHandles, boolean[] perUserInstalled,
+ int flags, PackageRemovedInfo outInfo, boolean writeSettings,
+ PackageParser.Package replacingPackage) {
+ if (deletedPkg.parentPackage != null) {
+ Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
+ return false;
+ }
+
final boolean applyUserRestrictions
= (allUserHandles != null) && (perUserInstalled != null);
- PackageSetting disabledPs = null;
+ final PackageSetting disabledPs;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
// reader
synchronized (mPackages) {
- disabledPs = mSettings.getDisabledSystemPkgLPr(newPs.name);
+ disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPkg.packageName);
}
- if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + newPs
+
+ if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
+ " disabledPs=" + disabledPs);
+
if (disabledPs == null) {
- Slog.w(TAG, "Attempt to delete unknown system package "+ newPs.name);
+ Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
return false;
} else if (DEBUG_REMOVE) {
Slog.d(TAG, "Deleting system pkg from data partition");
}
+
if (DEBUG_REMOVE) {
if (applyUserRestrictions) {
Slog.d(TAG, "Remembering install states:");
@@ -13742,27 +14192,30 @@
}
}
}
+
// Delete the updated package
outInfo.isRemovedPackageSystemUpdate = true;
- if (disabledPs.versionCode < newPs.versionCode) {
+ if (disabledPs.versionCode < deletedPs.versionCode) {
// Delete data for downgrades
flags &= ~PackageManager.DELETE_KEEP_DATA;
} else {
// Preserve data by setting flag
flags |= PackageManager.DELETE_KEEP_DATA;
}
- boolean ret = deleteInstalledPackageLI(newPs, true, flags,
- allUserHandles, perUserInstalled, outInfo, writeSettings);
+ boolean ret = deleteInstalledPackageLI(deletedPkg, true, flags, allUserHandles,
+ perUserInstalled, outInfo, writeSettings, replacingPackage);
if (!ret) {
return false;
}
+
// writer
synchronized (mPackages) {
// Reinstate the old system package
- mSettings.enableSystemPackageLPw(newPs.name);
+ enableSystemPackageLPw(disabledPs.pkg);
// Remove any native libraries from the upgraded package.
- NativeLibraryHelper.removeNativeBinariesLI(newPs.legacyNativeLibraryPathString);
+ removeNativeBinariesLI(deletedPkg);
}
+
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
int parseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
@@ -13774,7 +14227,8 @@
try {
newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
} catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to restore system package " + newPs.name + ": " + e.getMessage());
+ Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+ + e.getMessage());
return false;
}
@@ -13787,7 +14241,7 @@
// Propagate the permissions state as we do not want to drop on the floor
// runtime permissions. The update permissions method below will take
// care of removing obsolete permissions and grant install permissions.
- ps.getPermissionsState().copyFrom(newPs.getPermissionsState());
+ ps.getPermissionsState().copyFrom(deletedPs.getPermissionsState());
updatePermissionsLPw(newPkg.packageName, newPkg,
UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
@@ -13816,23 +14270,58 @@
return true;
}
- private boolean deleteInstalledPackageLI(PackageSetting ps,
- boolean deleteCodeAndResources, int flags,
- int[] allUserHandles, boolean[] perUserInstalled,
- PackageRemovedInfo outInfo, boolean writeSettings) {
- if (outInfo != null) {
- outInfo.uid = ps.appId;
+ private boolean deleteInstalledPackageLI(PackageParser.Package pkg,
+ boolean deleteCodeAndResources, int flags, int[] allUserHandles,
+ boolean[] perUserInstalled, PackageRemovedInfo outInfo, boolean writeSettings,
+ PackageParser.Package replacingPackage) {
+ PackageSetting ps = null;
+
+ synchronized (mPackages) {
+ pkg = mPackages.get(pkg.packageName);
+ if (pkg == null) {
+ return false;
+ }
+
+ ps = mSettings.mPackages.get(pkg.packageName);
+ if (ps == null) {
+ return false;
+ }
+
+ if (outInfo != null) {
+ outInfo.uid = ps.appId;
+ }
}
// Delete package data from internal structures and also remove data if flag is set
- removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags, writeSettings);
+ removePackageDataLI(ps, allUserHandles, perUserInstalled, outInfo, flags,
+ writeSettings);
- // Delete application code and resources
- if (deleteCodeAndResources && (outInfo != null)) {
- outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
- ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+ // Delete the child packages data
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageSetting childPs;
+ synchronized (mPackages) {
+ childPs = mSettings.peekPackageLPr(pkg.childPackages.get(i).packageName);
+ }
+ if (childPs != null) {
+ final int deleteFlags = (flags & DELETE_KEEP_DATA) != 0
+ && (replacingPackage != null
+ && !replacingPackage.hasChildPackage(childPs.name))
+ ? flags & ~DELETE_KEEP_DATA : flags;
+ removePackageDataLI(childPs, allUserHandles, perUserInstalled, outInfo,
+ deleteFlags, writeSettings);
+ }
}
+
+ // Delete application code and resources only for parent packages
+ if (ps.pkg.parentPackage == null) {
+ if (deleteCodeAndResources && (outInfo != null)) {
+ outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+ ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
+ if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+ }
+ }
+
return true;
}
@@ -13898,105 +14387,81 @@
*/
private boolean deletePackageLI(String packageName, UserHandle user,
boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,
- int flags, PackageRemovedInfo outInfo,
- boolean writeSettings) {
+ int flags, PackageRemovedInfo outInfo, boolean writeSettings,
+ PackageParser.Package replacingPackage) {
if (packageName == null) {
Slog.w(TAG, "Attempt to delete null packageName.");
return false;
}
+
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
+
PackageSetting ps;
- boolean dataOnly = false;
- int removeUser = -1;
- int appId = -1;
+
synchronized (mPackages) {
ps = mSettings.mPackages.get(packageName);
if (ps == null) {
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
return false;
}
- if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
- && user.getIdentifier() != UserHandle.USER_ALL) {
- // The caller is asking that the package only be deleted for a single
- // user. To do this, we just mark its uninstalled state and delete
- // its data. If this is a system app, we only allow this to happen if
- // they have set the special DELETE_SYSTEM_APP which requests different
- // semantics than normal for uninstalling system apps.
- if (DEBUG_REMOVE) Slog.d(TAG, "Only deleting for single user");
- final int userId = user.getIdentifier();
- ps.setUserState(userId,
- COMPONENT_ENABLED_STATE_DEFAULT,
- false, //installed
- true, //stopped
- true, //notLaunched
- false, //hidden
- false, //suspended
- null, null, null,
- false, // blockUninstall
- ps.readUserState(userId).domainVerificationStatus, 0);
- if (!isSystemApp(ps)) {
- // Do not uninstall the APK if an app should be cached
- boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
- if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
- // Other user still have this package installed, so all
- // we need to do is clear this user's data and save that
- // it is uninstalled.
- if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
- removeUser = user.getIdentifier();
- appId = ps.appId;
- scheduleWritePackageRestrictionsLocked(removeUser);
- } else {
- // We need to set it back to 'installed' so the uninstall
- // broadcasts will be sent correctly.
- if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
- ps.setInstalled(true, user.getIdentifier());
- }
- } else {
- // This is a system app, so we assume that the
- // other users still have this package installed, so all
+
+ if (ps.pkg.parentPackage != null && (!isSystemApp(ps)
+ || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"
+ + ((user == null) ? UserHandle.USER_ALL : user));
+ }
+ final int removedUserId = (user != null) ? user.getIdentifier()
+ : UserHandle.USER_ALL;
+ if (!clearPackageStateForUser(ps, removedUserId, outInfo)) {
+ return false;
+ }
+ markPackageUninstalledForUserLPw(ps, user);
+ scheduleWritePackageRestrictionsLocked(user);
+ return true;
+ }
+ }
+
+ if (((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null
+ && user.getIdentifier() != UserHandle.USER_ALL)) {
+ // The caller is asking that the package only be deleted for a single
+ // user. To do this, we just mark its uninstalled state and delete
+ // its data. If this is a system app, we only allow this to happen if
+ // they have set the special DELETE_SYSTEM_APP which requests different
+ // semantics than normal for uninstalling system apps.
+ markPackageUninstalledForUserLPw(ps, user);
+
+ if (!isSystemApp(ps)) {
+ // Do not uninstall the APK if an app should be cached
+ boolean keepUninstalledPackage = shouldKeepUninstalledPackageLPr(packageName);
+ if (ps.isAnyInstalled(sUserManager.getUserIds()) || keepUninstalledPackage) {
+ // Other user still have this package installed, so all
// we need to do is clear this user's data and save that
// it is uninstalled.
- if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
- removeUser = user.getIdentifier();
- appId = ps.appId;
- scheduleWritePackageRestrictionsLocked(removeUser);
+ if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
+ if (!clearPackageStateForUser(ps, user.getIdentifier(), outInfo)) {
+ return false;
+ }
+ scheduleWritePackageRestrictionsLocked(user);
+ return true;
+ } else {
+ // We need to set it back to 'installed' so the uninstall
+ // broadcasts will be sent correctly.
+ if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
+ ps.setInstalled(true, user.getIdentifier());
}
- }
- }
-
- if (removeUser >= 0) {
- // From above, we determined that we are deleting this only
- // for a single user. Continue the work here.
- if (DEBUG_REMOVE) Slog.d(TAG, "Updating install state for user: " + removeUser);
- if (outInfo != null) {
- outInfo.removedPackage = packageName;
- outInfo.removedAppId = appId;
- outInfo.removedUsers = new int[] {removeUser};
- }
- // TODO: triage flags as part of 26466827
- final int installerFlags = StorageManager.FLAG_STORAGE_CE
- | StorageManager.FLAG_STORAGE_DE;
- try {
- mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to delete app data", e);
- }
- removeKeystoreDataIfNeeded(removeUser, appId);
- schedulePackageCleaning(packageName, removeUser, false);
- synchronized (mPackages) {
- if (clearPackagePreferredActivitiesLPw(packageName, removeUser)) {
- scheduleWritePackageRestrictionsLocked(removeUser);
+ } else {
+ // This is a system app, so we assume that the
+ // other users still have this package installed, so all
+ // we need to do is clear this user's data and save that
+ // it is uninstalled.
+ if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
+ if (!clearPackageStateForUser(ps, user.getIdentifier(), outInfo)) {
+ return false;
}
- resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, removeUser);
+ scheduleWritePackageRestrictionsLocked(user);
+ return true;
}
- return true;
- }
-
- if (dataOnly) {
- // Delete application data first
- if (DEBUG_REMOVE) Slog.d(TAG, "Removing package data only");
- removePackageDataLI(ps, null, null, outInfo, flags, writeSettings);
- return true;
}
boolean ret = false;
@@ -14004,21 +14469,70 @@
if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
// When an updated system application is deleted we delete the existing resources as well and
// fall back to existing code in system partition
- ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,
- flags, outInfo, writeSettings);
+ ret = deleteSystemPackageLI(ps.pkg, ps, allUserHandles, perUserInstalled,
+ flags, outInfo, writeSettings, replacingPackage);
} else {
if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
// Kill application pre-emptively especially for apps on sd.
killApplication(packageName, ps.appId, "uninstall pkg");
- ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,
- allUserHandles, perUserInstalled,
- outInfo, writeSettings);
+ ret = deleteInstalledPackageLI(ps.pkg, deleteCodeAndResources, flags, allUserHandles,
+ perUserInstalled, outInfo, writeSettings, replacingPackage);
}
return ret;
}
- private final static class ClearStorageConnection implements ServiceConnection {
+ private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
+ final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
+ ? sUserManager.getUserIds() : new int[] {user.getIdentifier()};
+ for (int nextUserId : userIds) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Marking package:" + ps.name + " uninstalled for user:" + nextUserId);
+ }
+ ps.setUserState(nextUserId, COMPONENT_ENABLED_STATE_DEFAULT,
+ false /*installed*/, true /*stopped*/, true /*notLaunched*/,
+ false /*hidden*/, false /*suspended*/, null, null, null,
+ false /*blockUninstall*/,
+ ps.readUserState(nextUserId).domainVerificationStatus, 0);
+ }
+ }
+
+ private boolean clearPackageStateForUser(PackageSetting ps, int userId,
+ PackageRemovedInfo outInfo) {
+ final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+ : new int[] {userId};
+ for (int nextUserId : userIds) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
+ + nextUserId);
+ }
+ final int flags = StorageManager.FLAG_STORAGE_CE| StorageManager.FLAG_STORAGE_DE;
+ try {
+ mInstaller.destroyAppData(ps.volumeUuid, ps.name, nextUserId, flags);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Couldn't remove cache files for package " + ps.name, e);
+ return false;
+ }
+ removeKeystoreDataIfNeeded(nextUserId, ps.appId);
+ schedulePackageCleaning(ps.name, nextUserId, false);
+ synchronized (mPackages) {
+ if (clearPackagePreferredActivitiesLPw(ps.name, nextUserId)) {
+ scheduleWritePackageRestrictionsLocked(nextUserId);
+ }
+ resetUserChangesToRuntimePermissionsAndFlagsLPw(ps, nextUserId);
+ }
+ }
+
+ if (outInfo != null) {
+ outInfo.removedPackage = ps.name;
+ outInfo.removedAppId = ps.appId;
+ outInfo.removedUsers = userIds;
+ }
+
+ return true;
+ }
+
+ private final class ClearStorageConnection implements ServiceConnection {
IMediaContainerService mContainerService;
@Override
@@ -16708,7 +17222,7 @@
/*
* Unload packages mounted on external media. This involves deleting package
- * data from internal structures, sending broadcasts about diabled packages,
+ * data from internal structures, sending broadcasts about disabled packages,
* gc'ing to free up references, unmounting all secure containers
* corresponding to packages on external media, and posting a
* UPDATED_MEDIA_STATUS message if status has been requested. Please note
@@ -16730,7 +17244,7 @@
PackageRemovedInfo outInfo = new PackageRemovedInfo();
synchronized (mInstallLock) {
boolean res = deletePackageLI(pkgName, null, false, null, null,
- PackageManager.DELETE_KEEP_DATA, outInfo, false);
+ PackageManager.DELETE_KEEP_DATA, outInfo, false, null);
if (res) {
pkgList.add(pkgName);
} else {
@@ -16876,7 +17390,7 @@
final ApplicationInfo info = ps.pkg.applicationInfo;
final PackageRemovedInfo outInfo = new PackageRemovedInfo();
if (deletePackageLI(ps.name, null, false, null, null,
- PackageManager.DELETE_KEEP_DATA, outInfo, false)) {
+ PackageManager.DELETE_KEEP_DATA, outInfo, false, null)) {
unloaded.add(info);
} else {
Slog.w(TAG, "Failed to unload " + ps.codePath);
@@ -17121,6 +17635,15 @@
* left intact.
*/
private void prepareAppDataAfterInstall(PackageParser.Package pkg) {
+ prepareAppDataAfterInstallInternal(pkg);
+ final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPackage = pkg.childPackages.get(i);
+ prepareAppDataAfterInstallInternal(childPackage);
+ }
+ }
+
+ private void prepareAppDataAfterInstallInternal(PackageParser.Package pkg) {
final PackageSetting ps;
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index f106b62..e3866df 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -21,6 +21,7 @@
import android.content.pm.PackageParser;
import java.io.File;
+import java.util.List;
/**
* Settings data for a particular package we know about.
@@ -33,10 +34,11 @@
PackageSetting(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
- int pVersionCode, int pkgFlags, int privateFlags) {
+ int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
+ List<String> childPackageNames) {
super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- pVersionCode, pkgFlags, privateFlags);
+ pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames);
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 1117988..e5eec7e 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -28,6 +28,8 @@
import android.util.SparseArray;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
/**
* Settings base class for pending and resolved classes.
@@ -49,6 +51,9 @@
final String name;
final String realName;
+ String parentPackageName;
+ List<String> childPackageNames;
+
/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
@@ -126,10 +131,14 @@
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
- int pVersionCode, int pkgFlags, int pkgPrivateFlags) {
+ int pVersionCode, int pkgFlags, int pkgPrivateFlags,
+ String parentPackageName, List<String> childPackageNames) {
super(pkgFlags, pkgPrivateFlags);
this.name = name;
this.realName = realName;
+ this.parentPackageName = parentPackageName;
+ this.childPackageNames = (childPackageNames != null)
+ ? new ArrayList<>(childPackageNames) : null;
init(codePath, resourcePath, legacyNativeLibraryPathString, primaryCpuAbiString,
secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode);
}
@@ -174,6 +183,10 @@
volumeUuid = base.volumeUuid;
keySetData = new PackageKeySetData(base.keySetData);
+
+ parentPackageName = base.parentPackageName;
+ childPackageNames = (base.childPackageNames != null)
+ ? new ArrayList<>(base.childPackageNames) : null;
}
void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 9a20be7..f5c81e4 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -195,7 +195,7 @@
for (int i=0; i<mSignatures.length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- System.identityHashCode(mSignatures[i])));
+ mSignatures[i].hashCode()));
}
}
buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/PendingPackage.java b/services/core/java/com/android/server/pm/PendingPackage.java
index bb0dba1..da73085 100644
--- a/services/core/java/com/android/server/pm/PendingPackage.java
+++ b/services/core/java/com/android/server/pm/PendingPackage.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import java.io.File;
+import java.util.List;
final class PendingPackage extends PackageSettingBase {
final int sharedId;
@@ -24,10 +25,11 @@
PendingPackage(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString, int sharedId,
- int pVersionCode, int pkgFlags, int pkgPrivateFlags) {
+ int pVersionCode, int pkgFlags, int pkgPrivateFlags, String parentPackageName,
+ List<String> childPackageNames) {
super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- pVersionCode, pkgFlags, pkgPrivateFlags);
+ pVersionCode, pkgFlags, pkgPrivateFlags, parentPackageName, childPackageNames);
this.sharedId = sharedId;
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fcb777b..c1a5c5a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -184,6 +184,8 @@
private static final String TAG_SHARED_USER = "shared-user";
private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
private static final String TAG_PERMISSIONS = "perms";
+ private static final String TAG_CHILD_PACKAGE = "child-package";
+
private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
"persistent-preferred-activities";
static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
@@ -416,9 +418,23 @@
String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi,
int pkgFlags, int pkgPrivateFlags, UserHandle user, boolean add) {
final String name = pkg.packageName;
+ final String parentPackageName = (pkg.parentPackage != null)
+ ? pkg.parentPackage.packageName : null;
+
+ List<String> childPackageNames = null;
+ if (pkg.childPackages != null) {
+ final int childCount = pkg.childPackages.size();
+ childPackageNames = new ArrayList<>(childCount);
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = pkg.childPackages.get(i).packageName;
+ childPackageNames.add(childPackageName);
+ }
+ }
+
PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
resourcePath, legacyNativeLibraryPathString, primaryCpuAbi, secondaryCpuAbi,
- pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, add, true /* allowInstall */);
+ pkg.mVersionCode, pkgFlags, pkgPrivateFlags, user, add, true /* allowInstall */,
+ parentPackageName, childPackageNames);
return p;
}
@@ -503,8 +519,7 @@
return mSharedUsers.values();
}
-
- boolean disableSystemPackageLPw(String name) {
+ boolean disableSystemPackageLPw(String name, boolean replaced) {
final PackageSetting p = mPackages.get(name);
if(p == null) {
Log.w(PackageManagerService.TAG, "Package " + name + " is not an installed package");
@@ -512,18 +527,22 @@
}
final PackageSetting dp = mDisabledSysPackages.get(name);
// always make sure the system package code and resource paths dont change
- if (dp == null) {
+ if (dp == null && p.pkg != null && p.pkg.isSystemApp() && !p.pkg.isUpdatedSystemApp()) {
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
mDisabledSysPackages.put(name, p);
- // a little trick... when we install the new package, we don't
- // want to modify the existing PackageSetting for the built-in
- // version. so at this point we need a new PackageSetting that
- // is okay to muck with.
- PackageSetting newp = new PackageSetting(p);
- replacePackageLPw(name, newp);
+ if (replaced) {
+ // a little trick... when we install the new package, we don't
+ // want to modify the existing PackageSetting for the built-in
+ // version. so at this point we need a new PackageSetting that
+ // is okay to muck with.
+ PackageSetting newp = new PackageSetting(p);
+ replacePackageLPw(name, newp);
+ } else {
+ mPackages.remove(name);
+ }
return true;
}
return false;
@@ -542,7 +561,8 @@
PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
p.secondaryCpuAbiString, p.secondaryCpuAbiString,
- p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags);
+ p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
+ p.parentPackageName, p.childPackageNames);
mDisabledSysPackages.remove(name);
return ret;
}
@@ -557,7 +577,8 @@
PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
- String cpuAbiOverrideString, int uid, int vc, int pkgFlags, int pkgPrivateFlags) {
+ String cpuAbiOverrideString, int uid, int vc, int pkgFlags, int pkgPrivateFlags,
+ String parentPackageName, List<String> childPackageNames) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.appId == uid) {
@@ -569,7 +590,8 @@
}
p = new PackageSetting(name, realName, codePath, resourcePath,
legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags);
+ cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
+ childPackageNames);
p.appId = uid;
if (addUserIdLPw(uid, p, name)) {
mPackages.put(name, p);
@@ -650,7 +672,8 @@
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
- UserHandle installUser, boolean add, boolean allowInstall) {
+ UserHandle installUser, boolean add, boolean allowInstall, String parentPackage,
+ List<String> childPackageNames) {
PackageSetting p = mPackages.get(name);
UserManagerService userManager = UserManagerService.getInstance();
if (p != null) {
@@ -700,7 +723,8 @@
// We are consuming the data from an existing package.
p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags);
+ null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags,
+ parentPackage, childPackageNames);
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
+ name + " is adopting original package " + origPackage.name);
// Note that we will retain the new package's signature so
@@ -719,7 +743,8 @@
} else {
p = new PackageSetting(name, realName, codePath, resourcePath,
legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
- null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags);
+ null /* cpuAbiOverrideString */, vc, pkgFlags, pkgPrivateFlags,
+ parentPackage, childPackageNames);
p.setTimeStamp(codePath.lastModified());
p.sharedUser = sharedUser;
// If this is not a system app, it starts out stopped.
@@ -2049,6 +2074,20 @@
serializer.endTag(null, TAG_PERMISSIONS);
}
+ void writeChildPackagesLPw(XmlSerializer serializer, List<String> childPackageNames)
+ throws IOException {
+ if (childPackageNames == null) {
+ return;
+ }
+ final int childCount = childPackageNames.size();
+ for (int i = 0; i < childCount; i++) {
+ String childPackageName = childPackageNames.get(i);
+ serializer.startTag(null, TAG_CHILD_PACKAGE);
+ serializer.attribute(null, ATTR_NAME, childPackageName);
+ serializer.endTag(null, TAG_CHILD_PACKAGE);
+ }
+ }
+
// Note: assumed "stopped" field is already cleared in all packages.
// Legacy reader, used to read in the old file format after an upgrade. Not used after that.
void readStoppedLPw() {
@@ -2359,7 +2398,7 @@
}
final ApplicationInfo ai = pkg.pkg.applicationInfo;
- final String dataPath = new File(ai.dataDir).getCanonicalPath();
+ final String dataPath = ai.dataDir;
final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
final int[] gids = pkg.getPermissionsState().computeGids(userIds);
@@ -2449,6 +2488,12 @@
serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
+ if (pkg.parentPackageName != null) {
+ serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
+ }
+
+ writeChildPackagesLPw(serializer, pkg.childPackageNames);
+
// If this is a shared user, the permissions will be written there.
if (pkg.sharedUser == null) {
writePermissionsLPr(serializer, pkg.getPermissionsState()
@@ -2506,6 +2551,13 @@
if (pkg.volumeUuid != null) {
serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
}
+
+ if (pkg.parentPackageName != null) {
+ serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
+ }
+
+ writeChildPackagesLPw(serializer, pkg.childPackageNames);
+
pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
writePermissionsLPr(serializer, pkg.getPermissionsState()
@@ -2798,7 +2850,8 @@
(SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
pp.legacyNativeLibraryPathString, pp.primaryCpuAbiString,
pp.secondaryCpuAbiString, pp.versionCode, pp.pkgFlags, pp.pkgPrivateFlags,
- null, true /* add */, false /* allowInstall */);
+ null, true /* add */, false /* allowInstall */, pp.parentPackageName,
+ pp.childPackageNames);
if (p == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unable to create application package for " + pp.name);
@@ -3246,6 +3299,8 @@
String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi");
String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ String parentPackageName = parser.getAttributeValue(null, "parentPackageName");
+
String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi");
String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi");
String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride");
@@ -3275,7 +3330,8 @@
}
PackageSetting ps = new PackageSetting(name, realName, codePathFile,
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
- secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags);
+ secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
+ parentPackageName, null);
String timeStampStr = parser.getAttributeValue(null, "ft");
if (timeStampStr != null) {
try {
@@ -3324,6 +3380,12 @@
if (parser.getName().equals(TAG_PERMISSIONS)) {
readInstallPermissionsLPr(parser, ps.getPermissionsState());
+ } else if (parser.getName().equals(TAG_CHILD_PACKAGE)) {
+ String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
+ if (ps.childPackageNames == null) {
+ ps.childPackageNames = new ArrayList<>();
+ }
+ ps.childPackageNames.add(childPackageName);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <updated-package>: " + parser.getName());
@@ -3363,6 +3425,7 @@
PackageSettingBase packageSetting = null;
String version = null;
int versionCode = 0;
+ String parentPackageName;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -3374,6 +3437,8 @@
legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
+ parentPackageName = parser.getAttributeValue(null, "parentPackageName");
+
legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi");
secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
@@ -3494,7 +3559,7 @@
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
- pkgPrivateFlags);
+ pkgPrivateFlags, parentPackageName, null);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
@@ -3513,7 +3578,8 @@
packageSetting = new PendingPackage(name.intern(), realName, new File(
codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- userId, versionCode, pkgFlags, pkgPrivateFlags);
+ userId, versionCode, pkgFlags, pkgPrivateFlags, parentPackageName,
+ null);
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
packageSetting.lastUpdateTime = lastUpdateTime;
@@ -3575,6 +3641,7 @@
packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
}
}
+
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3621,6 +3688,12 @@
packageSetting.keySetData.addDefinedKeySet(id, alias);
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
readDomainVerificationLPw(parser, packageSetting);
+ } else if (tagName.equals(TAG_CHILD_PACKAGE)) {
+ String childPackageName = parser.getAttributeValue(null, ATTR_NAME);
+ if (packageSetting.childPackageNames == null) {
+ packageSetting.childPackageNames = new ArrayList<>();
+ }
+ packageSetting.childPackageNames.add(childPackageName);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
@@ -3884,6 +3957,28 @@
return mVerifierDeviceIdentity;
}
+ public boolean hasOtherDisabledSystemPkgWithChildLPr(String parentPackageName,
+ String childPackageName) {
+ final int packageCount = mDisabledSysPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ PackageSetting disabledPs = mDisabledSysPackages.valueAt(i);
+ if (disabledPs.childPackageNames == null || disabledPs.childPackageNames.isEmpty()) {
+ continue;
+ }
+ if (disabledPs.name.equals(parentPackageName)) {
+ continue;
+ }
+ final int childCount = disabledPs.childPackageNames.size();
+ for (int j = 0; j < childCount; j++) {
+ String currChildPackageName = disabledPs.childPackageNames.get(j);
+ if (currChildPackageName.equals(childPackageName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public PackageSetting getDisabledSystemPkgLPr(String name) {
PackageSetting ps = mDisabledSysPackages.get(name);
return ps;
@@ -4136,6 +4231,34 @@
}
pw.println();
if (ps.pkg != null) {
+ if (ps.pkg.parentPackage != null) {
+ PackageParser.Package parentPkg = ps.pkg.parentPackage;
+ PackageSetting pps = mPackages.get(parentPkg.packageName);
+ if (pps == null || !pps.codePathString.equals(parentPkg.codePath)) {
+ pps = mDisabledSysPackages.get(parentPkg.packageName);
+ }
+ if (pps != null) {
+ pw.print(prefix); pw.print(" parentPackage=");
+ pw.println(pps.realName != null ? pps.realName : pps.name);
+ }
+ } else if (ps.pkg.childPackages != null) {
+ pw.print(prefix); pw.print(" childPackages=[");
+ final int childCount = ps.pkg.childPackages.size();
+ for (int i = 0; i < childCount; i++) {
+ PackageParser.Package childPkg = ps.pkg.childPackages.get(i);
+ PackageSetting cps = mPackages.get(childPkg.packageName);
+ if (cps == null || !cps.codePathString.equals(childPkg.codePath)) {
+ cps = mDisabledSysPackages.get(childPkg.packageName);
+ }
+ if (cps != null) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ pw.print(cps.realName != null ? cps.realName : cps.name);
+ }
+ }
+ pw.println("]");
+ }
pw.print(prefix); pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, ps.pkg); pw.println();
pw.print(prefix); pw.print(" applicationInfo=");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a92cc31..c046ba6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2645,11 +2645,7 @@
}
}
} else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
- return R.anim.fade_in;
- } else if (transit == TRANSIT_EXIT) {
- return R.anim.fade_out;
- }
+ return selectDockedDividerAnimationLw(win, transit);
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -2669,6 +2665,24 @@
return 0;
}
+ private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+ int insets = mWindowManagerFuncs.getDockedDividerInsetsLw();
+
+ // If the divider is behind the navigation bar, don't animate.
+ if (mNavigationBar != null
+ && (win.getFrameLw().top + insets >= mNavigationBar.getFrameLw().top
+ || win.getFrameLw().left + insets >= mNavigationBar.getFrameLw().left)) {
+ return 0;
+ }
+ if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
+ return R.anim.fade_in;
+ } else if (transit == TRANSIT_EXIT) {
+ return R.anim.fade_out;
+ } else {
+ return 0;
+ }
+ }
+
@Override
public void selectRotationAnimationLw(int anim[]) {
if (PRINT_ANIM) Slog.i(TAG, "selectRotationAnimation mTopFullscreen="
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 3e99a4c..0f51c82e 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,6 +19,7 @@
import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -1582,7 +1583,7 @@
}
@Override
- public void startRecording(IBinder sessionToken, int userId) {
+ public void startRecording(IBinder sessionToken, @Nullable Uri programHint, int userId) {
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "startRecording");
@@ -1590,7 +1591,8 @@
try {
synchronized (mLock) {
try {
- getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording();
+ getSessionLocked(sessionToken, callingUid, resolvedUserId).startRecording(
+ programHint);
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in startRecording", e);
}
@@ -2474,7 +2476,8 @@
public void onSessionEvent(String eventType, Bundle eventArgs) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onEvent(what=" + eventType + ", data=" + eventArgs + ")");
+ Slog.d(TAG, "onEvent(eventType=" + eventType + ", eventArgs=" + eventArgs
+ + ")");
}
if (mSessionState.session == null || mSessionState.client == null) {
return;
@@ -2491,7 +2494,7 @@
public void onTimeShiftStatusChanged(int status) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onTimeShiftStatusChanged()");
+ Slog.d(TAG, "onTimeShiftStatusChanged(status=" + status + ")");
}
if (mSessionState.session == null || mSessionState.client == null) {
return;
@@ -2508,7 +2511,7 @@
public void onTimeShiftStartPositionChanged(long timeMs) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onTimeShiftStartPositionChanged()");
+ Slog.d(TAG, "onTimeShiftStartPositionChanged(timeMs=" + timeMs + ")");
}
if (mSessionState.session == null || mSessionState.client == null) {
return;
@@ -2525,7 +2528,7 @@
public void onTimeShiftCurrentPositionChanged(long timeMs) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onTimeShiftCurrentPositionChanged()");
+ Slog.d(TAG, "onTimeShiftCurrentPositionChanged(timeMs=" + timeMs + ")");
}
if (mSessionState.session == null || mSessionState.client == null) {
return;
@@ -2580,7 +2583,8 @@
public void onRecordingStopped(Uri recordedProgramUri) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onRecordingStopped()");
+ Slog.d(TAG, "onRecordingStopped(recordedProgramUri=" + recordedProgramUri
+ + ")");
}
if (mSessionState.session == null || mSessionState.client == null) {
return;
@@ -2598,7 +2602,7 @@
public void onError(int error) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onError()");
+ Slog.d(TAG, "onError(error=" + error + ")");
}
if (mSessionState.session == null || mSessionState.client == null) {
return;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7ec945d..93b1d62 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -127,6 +127,8 @@
boolean mAlwaysFocusable;
+ boolean mAppStopped;
+
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
@@ -311,6 +313,47 @@
}
}
+ // Here we destroy surfaces which have been marked as eligible by the animator, taking care
+ // to ensure the client has finished with them. If the client could still be using them
+ // we will skip destruction and try again when the client has stopped.
+ void destroySurfaces() {
+ final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
+ final DisplayContentList displayList = new DisplayContentList();
+ for (int i = allWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = allWindows.get(i);
+ if (!win.mDestroying) {
+ continue;
+ }
+
+ if (!mAppStopped && !win.mClientRemoveRequested) {
+ return;
+ }
+
+ win.destroyOrSaveSurface();
+ if (win.mRemoveOnExit) {
+ win.mExiting = false;
+ service.removeWindowInnerLocked(win);
+ }
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent != null && !displayList.contains(displayContent)) {
+ displayList.add(displayContent);
+ }
+ win.mDestroying = false;
+ }
+ for (int i = 0; i < displayList.size(); i++) {
+ final DisplayContent displayContent = displayList.get(i);
+ service.mLayersController.assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
+ // The application has stopped, so destroy any surfaces which were keeping alive
+ // in case they were still being used.
+ void notifyAppStopped() {
+ mAppStopped = true;
+ destroySurfaces();
+ }
+
/**
* Checks whether we should save surfaces for this app.
*
@@ -513,6 +556,9 @@
mFrozenBounds.remove();
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
+ if (!win.mHasSurface) {
+ continue;
+ }
win.mLayoutNeeded = true;
win.setDisplayLayoutNeeded();
if (!service.mResizingWindows.contains(win)) {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 1bfdcce..0678ca2 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -22,7 +22,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.util.ArrayMap;
@@ -41,8 +40,9 @@
*/
public class BoundsAnimationController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BoundsAnimationController" : TAG_WM;
+ private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
- // Only acccessed on UI thread.
+ // Only accessed on UI thread.
private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
private final class BoundsAnimator extends ValueAnimator
@@ -52,14 +52,22 @@
private final Rect mTo;
private final Rect mTmpRect;
private final boolean mMoveToFullScreen;
+ // True if this this animation was cancelled and will be replaced the another animation from
+ // the same {@link #AnimateBoundsUser} target.
+ private boolean mWillReplace;
+ // True to true if this animation replaced a previous animation of the same
+ // {@link #AnimateBoundsUser} target.
+ private final boolean mReplacement;
- BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen) {
+ BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to,
+ boolean moveToFullScreen, boolean replacement) {
super();
mTarget = target;
mFrom = from;
mTo = to;
mTmpRect = new Rect();
mMoveToFullScreen = moveToFullScreen;
+ mReplacement = replacement;
addUpdateListener(this);
addListener(this);
}
@@ -68,10 +76,10 @@
public void onAnimationUpdate(ValueAnimator animation) {
final float value = (Float) animation.getAnimatedValue();
final float remains = 1 - value;
- mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value);
- mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value);
- mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value);
- mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value);
+ mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
+ mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
+ mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
+ mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
if (DEBUG_ANIM) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + ", mBounds="
+ mTmpRect + ", from=" + mFrom + ", mTo=" + mTo + ", value=" + value
+ ", remains=" + remains);
@@ -85,13 +93,15 @@
@Override
public void onAnimationStart(Animator animation) {
-
+ if (!mReplacement) {
+ mTarget.onAnimationStart();
+ }
}
@Override
public void onAnimationEnd(Animator animation) {
finishAnimation();
- if (mMoveToFullScreen) {
+ if (mMoveToFullScreen && !mWillReplace) {
mTarget.moveToFullscreen();
}
}
@@ -101,8 +111,16 @@
finishAnimation();
}
+ @Override
+ public void cancel() {
+ mWillReplace = true;
+ super.cancel();
+ }
+
private void finishAnimation() {
- mTarget.finishBoundsAnimation();
+ if (!mWillReplace) {
+ mTarget.onAnimationEnd();
+ }
removeListener(this);
removeUpdateListener(this);
mRunningAnimations.remove(mTarget);
@@ -126,11 +144,13 @@
*/
boolean setSize(Rect bounds);
+ void onAnimationStart();
+
/**
- * Callback for the target to inform it that the animation is finished, so it can do some
+ * Callback for the target to inform it that the animation has ended, so it can do some
* necessary cleanup.
*/
- void finishBoundsAnimation();
+ void onAnimationEnd();
void moveToFullscreen();
@@ -146,13 +166,15 @@
}
final BoundsAnimator existing = mRunningAnimations.get(target);
- if (existing != null) {
+ final boolean replacing = existing != null;
+ if (replacing) {
existing.cancel();
}
- BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen);
+ final BoundsAnimator animator =
+ new BoundsAnimator(target, from, to, moveToFullscreen, replacing);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
- animator.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ animator.setDuration(DEFAULT_APP_TRANSITION_DURATION * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
animator.setInterpolator(new LinearInterpolator());
animator.start();
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 7295318..412a455 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -72,6 +72,10 @@
return mDividerWindowWidth - 2 * mDividerInsets;
}
+ int getContentInsets() {
+ return mDividerInsets;
+ }
+
void setResizing(boolean resizing) {
mResizing = resizing;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a06d3fc..4167ac4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -540,7 +540,7 @@
}
boolean isDragResizing() {
- return mDragResizing;
+ return mDragResizing || (mStack != null && mStack.isDragResizing());
}
void updateDisplayInfo(final DisplayContent displayContent) {
@@ -584,7 +584,7 @@
final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState win = windows.get(winNdx);
- if (!resizingWindows.contains(win)) {
+ if (win.mHasSurface && !resizingWindows.contains(win)) {
if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
resizingWindows.add(win);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 40ca1c5..a8b72892 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -99,6 +99,9 @@
boolean mDeferDetach;
private boolean mUpdateBoundsAfterRotation = false;
+ // Whether the stack and all its tasks is currently being drag-resized
+ private boolean mDragResizing;
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
@@ -911,6 +914,10 @@
return false;
}
+ boolean isDragResizing() {
+ return mDragResizing;
+ }
+
@Override // AnimatesBounds
public boolean setSize(Rect bounds) {
synchronized (mService.mWindowMap) {
@@ -926,16 +933,17 @@
}
@Override // AnimatesBounds
- public void finishBoundsAnimation() {
+ public void onAnimationStart() {
synchronized (mService.mWindowMap) {
- if (mTasks.isEmpty()) {
- return;
- }
- final Task task = mTasks.get(mTasks.size() - 1);
- if (task != null) {
- task.setDragResizing(false);
- mService.requestTraversal();
- }
+ mDragResizing = true;
+ }
+ }
+
+ @Override // AnimatesBounds
+ public void onAnimationEnd() {
+ synchronized (mService.mWindowMap) {
+ mDragResizing = false;
+ mService.requestTraversal();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ae6c89a..d1ffaa0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2154,6 +2154,14 @@
if (win == null) {
return;
}
+ // We set this here instead of removeWindowLocked because we only want it to be
+ // true when the client has requested we remove the window. In other remove
+ // cases, we have to wait for activity stop to safely remove the window (as the
+ // client may still be using the surface). In this case though, the client has
+ // just dismissed a window (for example a Dialog) and activity stop isn't
+ // necessarily imminent, so we need to know not to wait for it after our
+ // hanimation (if applicable) finishes.
+ win.mClientRemoveRequested = true;
removeWindowLocked(win);
}
}
@@ -4188,6 +4196,24 @@
}
@Override
+ public void notifyAppStopped(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "notifyAppStopped()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ }
+
+ synchronized(mWindowMap) {
+ final AppWindowToken wtoken;
+ wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
+ return;
+ }
+ wtoken.notifyAppStopped();
+ }
+ }
+
+ @Override
public void setAppVisibility(IBinder token, boolean visible) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppVisibility()")) {
@@ -4210,6 +4236,7 @@
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
+ wtoken.mAppStopped = false;
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
@@ -9592,6 +9619,11 @@
&& !appWindow.mTask.inFreeformWorkspace();
}
+ @Override
+ public int getDockedDividerInsetsLw() {
+ return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
+ }
+
void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
mPolicy.dump(" ", pw, args);
@@ -10350,13 +10382,6 @@
Slog.w(TAG, "animateResizePinnedStack: stackId " + PINNED_STACK_ID + " not found.");
return;
}
- final ArrayList<Task> tasks = stack.getTasks();
- if (tasks.isEmpty()) {
- Slog.w(TAG, "animateResizePinnedStack: pinned stack doesn't have any tasks.");
- return;
- }
- final Task task = tasks.get(tasks.size() - 1);
- task.setDragResizing(true);
final Rect originalBounds = new Rect();
stack.getBounds(originalBounds);
UiThread.getHandler().post(new Runnable() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 880514c..3430b34 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -386,6 +386,13 @@
boolean mRemoved;
/**
+ * Has the client requested we remove the window? In this case we know
+ * that we can dispose of it when we wish without further synchronization
+ * with the client
+ */
+ boolean mClientRemoveRequested;
+
+ /**
* Temp for keeping track of windows that have been removed when
* rebuilding window list.
*/
@@ -2145,7 +2152,7 @@
// background.
return (mDisplayContent.mDividerControllerLocked.isResizing()
|| mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
- !task.inFreeformWorkspace();
+ !task.inFreeformWorkspace() && isVisibleLw();
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c7c9cbf..0201296 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -471,16 +471,31 @@
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Exit animation finished in " + this
+ ": remove=" + mWin.mRemoveOnExit);
- if (hasSurface()) {
- mService.mDestroySurface.add(mWin);
- mWin.mDestroying = true;
- hide("finishExit");
+
+
+ mWin.mDestroying = true;
+
+ // If we have an app token, we ask it to destroy the surface for us,
+ // so that it can take care to ensure the activity has actually stopped
+ // and the surface is not still in use. Otherwise we add the service to
+ // mDestroySurface and allow it to be processed in our next transaction.
+ if (mWin.mAppToken != null) {
+ if (hasSurface()) {
+ hide("finishExit");
+ }
+ mWin.mAppToken.destroySurfaces();
+ } else {
+ if (hasSurface()) {
+ mService.mDestroySurface.add(mWin);
+ hide("finishExit");
+ }
+ mWin.mExiting = false;
+ if (mWin.mRemoveOnExit) {
+ mService.mPendingRemove.add(mWin);
+ mWin.mRemoveOnExit = false;
+ }
}
- mWin.mExiting = false;
- if (mWin.mRemoveOnExit) {
- mService.mPendingRemove.add(mWin);
- mWin.mRemoveOnExit = false;
- }
+
mWallpaperControllerLocked.hideWallpapers(mWin);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 7f9a0de..ba83be1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -39,7 +39,7 @@
public PackageSetting generateFakePackageSetting(String name) {
return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
- "", 1, 0, 0);
+ "", 1, 0, 0, null, null);
}
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c122c5a..f1cbb9a 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -774,9 +774,9 @@
}
/**
- * Register a {@link PhoneAccount} for use by the system. When registering
- * {@link PhoneAccount}s, existing registrations will be overwritten if the
- * {@link PhoneAccountHandle} matches that of a {@link PhoneAccount} which is already
+ * Register a {@link PhoneAccount} for use by the system that will be stored in Device Encrypted
+ * storage. When registering {@link PhoneAccount}s, existing registrations will be overwritten
+ * if the {@link PhoneAccountHandle} matches that of a {@link PhoneAccount} which is already
* registered. Once registered, the {@link PhoneAccount} is listed to the user as an option
* when placing calls. The user may still need to enable the {@link PhoneAccount} within
* the phone app settings before the account is usable.
@@ -1166,11 +1166,16 @@
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
* has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
- * with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
- * to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
- * additional information about the call (See
- * {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
- *
+ * with {@link #registerPhoneAccount} and the user must have enabled the corresponding
+ * {@link PhoneAccount}. This can be checked using {@link #getPhoneAccount}. Once invoked, this
+ * method will cause the system to bind to the {@link ConnectionService} associated with the
+ * {@link PhoneAccountHandle} and request additional information about the call
+ * (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
+ * call UI.
+ * <p>
+ * A {@link SecurityException} will be thrown if either the {@link PhoneAccountHandle} does not
+ * correspond to a registered {@link PhoneAccount} or the associated {@link PhoneAccount} is not
+ * currently enabled by the user.
* @param phoneAccount A {@link PhoneAccountHandle} registered with
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index f74b93a..88b6270 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -43,6 +43,9 @@
link/TableMerger.cpp \
link/XmlReferenceLinker.cpp \
process/SymbolTable.cpp \
+ proto/ProtoHelpers.cpp \
+ proto/TableProtoDeserializer.cpp \
+ proto/TableProtoSerializer.cpp \
unflatten/BinaryResourceParser.cpp \
unflatten/ResChunkPullParser.cpp \
util/BigBuffer.cpp \
@@ -67,13 +70,14 @@
xml/XmlPullParser.cpp \
xml/XmlUtil.cpp
+sources += Format.proto
+
testSources := \
compile/IdAssigner_test.cpp \
compile/PseudolocaleGenerator_test.cpp \
compile/Pseudolocalizer_test.cpp \
compile/XmlIdCollector_test.cpp \
filter/ConfigFilter_test.cpp \
- flatten/FileExportWriter_test.cpp \
flatten/TableFlattener_test.cpp \
flatten/XmlFlattener_test.cpp \
link/AutoVersioner_test.cpp \
@@ -83,7 +87,7 @@
link/TableMerger_test.cpp \
link/XmlReferenceLinker_test.cpp \
process/SymbolTable_test.cpp \
- unflatten/FileExportHeaderReader_test.cpp \
+ proto/TableProtoSerializer_test.cpp \
util/BigBuffer_test.cpp \
util/Maybe_test.cpp \
util/StringPiece_test.cpp \
@@ -105,6 +109,7 @@
toolSources := \
compile/Compile.cpp \
+ dump/Dump.cpp \
link/Link.cpp
hostLdLibs :=
@@ -119,6 +124,9 @@
libpng \
libbase
+hostSharedLibs := \
+ libprotobuf-cpp-lite
+
ifneq ($(strip $(USE_MINGW)),)
hostStaticLibs += libz
else
@@ -127,21 +135,23 @@
cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
+protoIncludes := $(call generated-sources-dir-for,STATIC_LIBRARIES,libaapt2,HOST)
# ==========================================================
# Build the host static library: libaapt2
# ==========================================================
include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
LOCAL_MODULE := libaapt2
LOCAL_SRC_FILES := $(sources)
LOCAL_STATIC_LIBRARIES += $(hostStaticLibs)
LOCAL_CFLAGS += $(cFlags)
LOCAL_CPPFLAGS += $(cppFlags)
+LOCAL_C_INCLUDES += $(protoIncludes)
include $(BUILD_HOST_STATIC_LIBRARY)
-
# ==========================================================
# Build the host tests: libaapt2_tests
# ==========================================================
@@ -152,9 +162,11 @@
LOCAL_SRC_FILES := $(testSources)
LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
+LOCAL_SHARED_LIBRARIES += $(hostSharedLibs)
LOCAL_LDLIBS += $(hostLdLibs)
LOCAL_CFLAGS += $(cFlags)
LOCAL_CPPFLAGS += $(cppFlags)
+LOCAL_C_INCLUDES += $(protoIncludes)
include $(BUILD_HOST_NATIVE_TEST)
@@ -167,9 +179,11 @@
LOCAL_SRC_FILES := $(main) $(toolSources)
LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
+LOCAL_SHARED_LIBRARIES += $(hostSharedLibs)
LOCAL_LDLIBS += $(hostLdLibs)
LOCAL_CFLAGS += $(cFlags)
LOCAL_CPPFLAGS += $(cppFlags)
+LOCAL_C_INCLUDES += $(protoIncludes)
include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/aapt2/Format.proto b/tools/aapt2/Format.proto
new file mode 100644
index 0000000..d05425c
--- /dev/null
+++ b/tools/aapt2/Format.proto
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package aapt.pb;
+
+message ConfigDescription {
+ optional bytes data = 1;
+ optional string product = 2;
+}
+
+message StringPool {
+ optional bytes data = 1;
+}
+
+message CompiledFile {
+ message Symbol {
+ optional string resource_name = 1;
+ optional uint32 line_no = 2;
+ }
+
+ optional string resource_name = 1;
+ optional ConfigDescription config = 2;
+ optional string source_path = 3;
+ repeated Symbol exported_symbols = 4;
+}
+
+message ResourceTable {
+ optional StringPool string_pool = 1;
+ optional StringPool source_pool = 2;
+ optional StringPool symbol_pool = 3;
+ repeated Package packages = 4;
+}
+
+message Package {
+ optional uint32 package_id = 1;
+ optional string package_name = 2;
+ repeated Type types = 3;
+}
+
+message Type {
+ optional uint32 id = 1;
+ optional string name = 2;
+ repeated Entry entries = 3;
+}
+
+message SymbolStatus {
+ enum Visibility {
+ Unknown = 0;
+ Private = 1;
+ Public = 2;
+ }
+ optional Visibility visibility = 1;
+ optional Source source = 2;
+ optional string comment = 3;
+}
+
+message Entry {
+ optional uint32 id = 1;
+ optional string name = 2;
+ optional SymbolStatus symbol_status = 3;
+ repeated ConfigValue config_values = 4;
+}
+
+message ConfigValue {
+ optional ConfigDescription config = 1;
+ optional Value value = 2;
+}
+
+message Source {
+ optional uint32 path_idx = 1;
+ optional uint32 line_no = 2;
+ optional uint32 col_no = 3;
+}
+
+message Reference {
+ enum Type {
+ Ref = 0;
+ Attr = 1;
+ }
+ optional Type type = 1;
+ optional uint32 id = 2;
+ optional uint32 symbol_idx = 3;
+ optional bool private = 4;
+}
+
+message Id {
+}
+
+message String {
+ optional uint32 idx = 1;
+}
+
+message RawString {
+ optional uint32 idx = 1;
+}
+
+message FileReference {
+ optional uint32 path_idx = 1;
+}
+
+message Primitive {
+ optional uint32 type = 1;
+ optional uint32 data = 2;
+}
+
+message Attribute {
+ message Symbol {
+ optional Source source = 1;
+ optional string comment = 2;
+ optional Reference name = 3;
+ optional uint32 value = 4;
+ }
+ optional uint32 format_flags = 1;
+ optional int32 min_int = 2;
+ optional int32 max_int = 3;
+ repeated Symbol symbols = 4;
+}
+
+message Style {
+ message Entry {
+ optional Source source = 1;
+ optional string comment = 2;
+ optional Reference key = 3;
+ optional Item item = 4;
+ }
+
+ optional Reference parent = 1;
+ optional Source parent_source = 2;
+ repeated Entry entries = 3;
+}
+
+message Styleable {
+ message Entry {
+ optional Source source = 1;
+ optional string comment = 2;
+ optional Reference attr = 3;
+ }
+ repeated Entry entries = 1;
+}
+
+message Array {
+ message Entry {
+ optional Source source = 1;
+ optional string comment = 2;
+ optional Item item = 3;
+ }
+ repeated Entry entries = 1;
+}
+
+message Plural {
+ enum Arity {
+ Zero = 0;
+ One = 1;
+ Two = 2;
+ Few = 3;
+ Many = 4;
+ Other = 5;
+ }
+
+ message Entry {
+ optional Source source = 1;
+ optional string comment = 2;
+ optional Arity arity = 3;
+ optional Item item = 4;
+ }
+ repeated Entry entries = 1;
+}
+
+message Item {
+ optional Reference ref = 1;
+ optional String str = 2;
+ optional RawString raw_str = 3;
+ optional FileReference file = 4;
+ optional Id id = 5;
+ optional Primitive prim = 6;
+}
+
+message CompoundValue {
+ optional Attribute attr = 1;
+ optional Style style = 2;
+ optional Styleable styleable = 3;
+ optional Array array = 4;
+ optional Plural plural = 5;
+}
+
+message Value {
+ optional Source source = 1;
+ optional string comment = 2;
+ optional bool weak = 3;
+
+ optional Item item = 4;
+ optional CompoundValue compound_value = 5;
+}
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 248e7ad..a2fadd9 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -23,6 +23,7 @@
extern int compile(const std::vector<StringPiece>& args);
extern int link(const std::vector<StringPiece>& args);
+extern int dump(const std::vector<StringPiece>& args);
} // namespace aapt
@@ -41,12 +42,14 @@
return aapt::compile(args);
} else if (command == "link" || command == "l") {
return aapt::link(args);
+ } else if (command == "dump" || command == "d") {
+ return aapt::dump(args);
}
std::cerr << "unknown command '" << command << "'\n";
} else {
std::cerr << "no command specified\n";
}
- std::cerr << "\nusage: aapt2 [compile|link] ..." << std::endl;
+ std::cerr << "\nusage: aapt2 [compile|link|dump] ..." << std::endl;
return 1;
}
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 07f62af..74c48b0 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -51,6 +51,10 @@
}
bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) {
+ if (str.empty()) {
+ return false;
+ }
+
size_t offset = 0;
bool priv = false;
if (str.data()[0] == u'*') {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 64ca971..a0fbcc6 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -45,7 +45,8 @@
* `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix
* was present.
*/
-bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, bool* outPrivate);
+bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource,
+ bool* outPrivate = nullptr);
/*
* Returns true if the string was parsed as a reference (@[+][package:]type/name), with
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index c9f93e1..7425f97 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -58,6 +58,8 @@
EXPECT_TRUE(ResourceUtils::parseResourceName(u"*android:color/foo", &actual, &actualPriv));
EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual);
EXPECT_TRUE(actualPriv);
+
+ EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece16(), &actual, &actualPriv));
}
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b93e6d8..ab9c792 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -19,7 +19,6 @@
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
-#include "flatten/ResourceTypeExtensions.h"
#include <androidfw/ResourceTypes.h>
#include <limits>
@@ -47,7 +46,7 @@
}
bool RawString::flatten(android::Res_value* outValue) const {
- outValue->dataType = ExtendedTypes::TYPE_RAW_STRING;
+ outValue->dataType = android::Res_value::TYPE_STRING;
outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
return true;
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 8e317db..dc2e28e 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -154,8 +154,8 @@
bool privateReference = false;
Reference();
- Reference(const ResourceNameRef& n, Type type = Type::kResource);
- Reference(const ResourceId& i, Type type = Type::kResource);
+ explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
+ explicit Reference(const ResourceId& i, Type type = Type::kResource);
bool flatten(android::Res_value* outValue) const override;
Reference* clone(StringPool* newPool) const override;
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 94042e3..5493039 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -18,6 +18,7 @@
#define AAPT_VALUE_VISITOR_H
#include "ResourceValues.h"
+#include "ResourceTable.h"
namespace aapt {
@@ -140,6 +141,23 @@
return visitor.value;
}
+
+inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ for (auto& configValue : entry->values) {
+ configValue.value->accept(visitor);
+ }
+ }
+ }
+}
+
+inline void visitAllValuesInTable(ResourceTable* table, RawValueVisitor* visitor) {
+ for (auto& pkg : table->packages) {
+ visitAllValuesInPackage(pkg.get(), visitor);
+ }
+}
+
} // namespace aapt
#endif // AAPT_VALUE_VISITOR_H
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 689ace6..1eefb82 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -24,15 +24,17 @@
#include "compile/PseudolocaleGenerator.h"
#include "compile/XmlIdCollector.h"
#include "flatten/Archive.h"
-#include "flatten/FileExportWriter.h"
-#include "flatten/TableFlattener.h"
#include "flatten/XmlFlattener.h"
+#include "proto/ProtoSerialize.h"
#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <google/protobuf/io/coded_stream.h>
+
#include <dirent.h>
#include <fstream>
#include <string>
@@ -232,34 +234,95 @@
}
}
- // Assign IDs to prepare the table for flattening.
- IdAssigner idAssigner;
- if (!idAssigner.consume(context, &table)) {
- return false;
- }
-
- // Flatten the table.
- BigBuffer buffer(1024);
- TableFlattenerOptions tableFlattenerOptions;
- tableFlattenerOptions.useExtendedChunks = true;
- TableFlattener flattener(&buffer, tableFlattenerOptions);
- if (!flattener.consume(context, &table)) {
- return false;
- }
-
+ // Create the file/zip entry.
if (!writer->startEntry(outputPath, 0)) {
context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
return false;
}
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
+
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream interface.
+ {
+ google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+
+ if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+ return false;
}
}
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
+ if (!writer->finishEntry()) {
+ context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
+ return false;
+ }
+ return true;
+}
+
+static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
+ const BigBuffer& buffer, IArchiveWriter* writer,
+ IDiagnostics* diag) {
+ // Start the entry so we can write the header.
+ if (!writer->startEntry(outputPath, 0)) {
+ diag->error(DiagMessage(outputPath) << "failed to open file");
+ return false;
+ }
+
+ // Create the header.
+ std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
+
+ {
+ // The stream must be destroyed before we finish the entry, or else
+ // some data won't be flushed.
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
+ // interface.
+ google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+ CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
+ for (const BigBuffer::Block& block : buffer) {
+ if (!outputStream.Write(block.buffer.get(), block.size)) {
+ diag->error(DiagMessage(outputPath) << "failed to write data");
+ return false;
+ }
+ }
+ }
+
+ if (!writer->finishEntry()) {
+ diag->error(DiagMessage(outputPath) << "failed to finish writing data");
+ return false;
+ }
+ return true;
+}
+
+static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
+ const android::FileMap& map, IArchiveWriter* writer,
+ IDiagnostics* diag) {
+ // Start the entry so we can write the header.
+ if (!writer->startEntry(outputPath, 0)) {
+ diag->error(DiagMessage(outputPath) << "failed to open file");
+ return false;
+ }
+
+ // Create the header.
+ std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(file);
+
+ {
+ // The stream must be destroyed before we finish the entry, or else
+ // some data won't be flushed.
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
+ // interface.
+ google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+ CompiledFileOutputStream outputStream(&adaptor, pbCompiledFile.get());
+ if (!outputStream.Write(map.getDataPtr(), map.getDataLength())) {
+ diag->error(DiagMessage(outputPath) << "failed to write data");
+ return false;
+ }
+ }
+
+ if (!writer->finishEntry()) {
+ diag->error(DiagMessage(outputPath) << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
static bool compileXml(IAaptContext* context, const CompileOptions& options,
@@ -267,7 +330,6 @@
const std::string& outputPath) {
std::unique_ptr<xml::XmlResource> xmlRes;
-
{
std::ifstream fin(pathData.source.path, std::ifstream::binary);
if (!fin) {
@@ -295,30 +357,18 @@
xmlRes->file.source = pathData.source;
BigBuffer buffer(1024);
- ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &xmlRes->file);
-
XmlFlattenerOptions xmlFlattenerOptions;
xmlFlattenerOptions.keepRawValues = true;
- XmlFlattener flattener(fileExportWriter.getBuffer(), xmlFlattenerOptions);
+ XmlFlattener flattener(&buffer, xmlFlattenerOptions);
if (!flattener.consume(context, xmlRes.get())) {
return false;
}
- fileExportWriter.finish();
-
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
+ if (!writeHeaderAndBufferToWriter(outputPath, xmlRes->file, buffer, writer,
+ context->getDiagnostics())) {
return false;
}
-
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
-
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
+ return true;
}
static bool compilePng(IAaptContext* context, const CompileOptions& options,
@@ -330,8 +380,6 @@
resFile.config = pathData.config;
resFile.source = pathData.source;
- ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
-
{
std::ifstream fin(pathData.source.path, std::ifstream::binary);
if (!fin) {
@@ -340,26 +388,16 @@
}
Png png(context->getDiagnostics());
- if (!png.process(pathData.source, &fin, fileExportWriter.getBuffer(), {})) {
+ if (!png.process(pathData.source, &fin, &buffer, {})) {
return false;
}
}
- fileExportWriter.finish();
-
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
+ if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
+ context->getDiagnostics())) {
return false;
}
-
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
-
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
+ return true;
}
static bool compileFile(IAaptContext* context, const CompileOptions& options,
@@ -371,8 +409,6 @@
resFile.config = pathData.config;
resFile.source = pathData.source;
- ChunkWriter fileExportWriter = wrapBufferWithFileExportHeader(&buffer, &resFile);
-
std::string errorStr;
Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
if (!f) {
@@ -380,35 +416,10 @@
return false;
}
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
+ if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
+ context->getDiagnostics())) {
return false;
}
-
- // Manually set the size and don't call finish(). This is because we are not copying from
- // the buffer the entire file.
- fileExportWriter.getChunkHeader()->size =
- util::hostToDevice32(buffer.size() + f.value().getDataLength());
-
- if (!writer->writeEntry(buffer)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
- }
-
- // Only write if we have something to write. This is because mmap fails with length of 0,
- // but we still want to compile the file to get the resource ID.
- if (f.value().getDataPtr() && f.value().getDataLength() > 0) {
- if (!writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
- }
-
return true;
}
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 80c6bbc..aa4a580 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -64,14 +64,12 @@
// Mark entry ID as taken.
if (!usedEntryIds.insert(entry->id.value()).second) {
// This ID existed before!
- ResourceNameRef nameRef =
- { package->name, type->type, entry->name };
- ResourceId takenId(package->id.value(), type->id.value(),
- entry->id.value());
+ ResourceNameRef nameRef(package->name, type->type, entry->name);
context->getDiagnostics()->error(DiagMessage()
<< "resource '" << nameRef << "' "
- << "has duplicate ID '"
- << takenId << "'");
+ << "has duplicate entry ID "
+ << std::hex << (int) entry->id.value()
+ << std::dec);
return false;
}
}
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
new file mode 100644
index 0000000..915fae8
--- /dev/null
+++ b/tools/aapt2/dump/Dump.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#include "Debug.h"
+#include "Diagnostics.h"
+#include "Flags.h"
+#include "process/IResourceTableConsumer.h"
+#include "proto/ProtoSerialize.h"
+#include "util/Files.h"
+#include "util/StringPiece.h"
+
+#include <vector>
+
+namespace aapt {
+
+//struct DumpOptions {
+//
+//};
+
+void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data, size_t len,
+ const Source& source, IAaptContext* context) {
+ std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(pbFile, source,
+ context->getDiagnostics());
+ if (!file) {
+ return;
+ }
+
+ std::cout << "Resource: " << file->name << "\n"
+ << "Config: " << file->config << "\n"
+ << "Source: " << file->source << "\n";
+}
+
+void dumpCompiledTable(const pb::ResourceTable& pbTable, const Source& source,
+ IAaptContext* context) {
+ std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
+ context->getDiagnostics());
+ if (!table) {
+ return;
+ }
+
+ Debug::printTable(table.get());
+}
+
+void tryDumpFile(IAaptContext* context, const std::string& filePath) {
+ std::string err;
+ Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
+ if (!file) {
+ context->getDiagnostics()->error(DiagMessage(filePath) << err);
+ return;
+ }
+
+ android::FileMap* fileMap = &file.value();
+
+ // Try as a compiled table.
+ pb::ResourceTable pbTable;
+ if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) {
+ dumpCompiledTable(pbTable, Source(filePath), context);
+ return;
+ }
+
+ // Try as a compiled file.
+ CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength());
+ if (const pb::CompiledFile* pbFile = input.CompiledFile()) {
+ dumpCompiledFile(*pbFile, input.data(), input.size(), Source(filePath), context);
+ return;
+ }
+}
+
+class DumpContext : public IAaptContext {
+public:
+ IDiagnostics* getDiagnostics() override {
+ return &mDiagnostics;
+ }
+
+ NameMangler* getNameMangler() override {
+ abort();
+ return nullptr;
+ }
+
+ StringPiece16 getCompilationPackage() override {
+ return {};
+ }
+
+ uint8_t getPackageId() override {
+ return 0;
+ }
+
+ ISymbolTable* getExternalSymbols() override {
+ abort();
+ return nullptr;
+ }
+
+private:
+ StdErrDiagnostics mDiagnostics;
+};
+
+/**
+ * Entry point for dump command.
+ */
+int dump(const std::vector<StringPiece>& args) {
+ //DumpOptions options;
+ Flags flags = Flags();
+ if (!flags.parse("aapt2 dump", args, &std::cerr)) {
+ return 1;
+ }
+
+ DumpContext context;
+
+ for (const std::string& arg : flags.getArgs()) {
+ tryDumpFile(&context, arg);
+ }
+ return 0;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
index 6da1d2a..34c10ad 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -22,6 +22,7 @@
#include "util/Files.h"
#include "util/StringPiece.h"
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <fstream>
#include <memory>
#include <string>
@@ -40,13 +41,18 @@
size_t uncompressedSize;
};
-struct IArchiveWriter {
+struct IArchiveWriter : public google::protobuf::io::CopyingOutputStream {
virtual ~IArchiveWriter() = default;
virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
virtual bool writeEntry(const BigBuffer& buffer) = 0;
virtual bool writeEntry(const void* data, size_t len) = 0;
virtual bool finishEntry() = 0;
+
+ // CopyingOutputStream implementations.
+ bool Write(const void* buffer, int size) override {
+ return writeEntry(buffer, size);
+ }
};
std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
diff --git a/tools/aapt2/flatten/FileExportWriter.h b/tools/aapt2/flatten/FileExportWriter.h
deleted file mode 100644
index 7688fa7..0000000
--- a/tools/aapt2/flatten/FileExportWriter.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_FLATTEN_FILEEXPORTWRITER_H
-#define AAPT_FLATTEN_FILEEXPORTWRITER_H
-
-#include "StringPool.h"
-
-#include "flatten/ResourceTypeExtensions.h"
-#include "flatten/ChunkWriter.h"
-#include "process/IResourceTableConsumer.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <utils/misc.h>
-
-namespace aapt {
-
-static ChunkWriter wrapBufferWithFileExportHeader(BigBuffer* buffer, ResourceFile* res) {
- ChunkWriter fileExportWriter(buffer);
- FileExport_header* fileExport = fileExportWriter.startChunk<FileExport_header>(
- RES_FILE_EXPORT_TYPE);
-
- ExportedSymbol* symbolRefs = nullptr;
- if (!res->exportedSymbols.empty()) {
- symbolRefs = fileExportWriter.nextBlock<ExportedSymbol>(
- res->exportedSymbols.size());
- }
- fileExport->exportedSymbolCount = util::hostToDevice32(res->exportedSymbols.size());
-
- StringPool symbolExportPool;
- memcpy(fileExport->magic, "AAPT", NELEM(fileExport->magic));
- fileExport->config = res->config;
- fileExport->config.swapHtoD();
- fileExport->name.index = util::hostToDevice32(symbolExportPool.makeRef(res->name.toString())
- .getIndex());
- fileExport->source.index = util::hostToDevice32(symbolExportPool.makeRef(util::utf8ToUtf16(
- res->source.path)).getIndex());
-
- for (const SourcedResourceName& name : res->exportedSymbols) {
- symbolRefs->name.index = util::hostToDevice32(symbolExportPool.makeRef(name.name.toString())
- .getIndex());
- symbolRefs->line = util::hostToDevice32(name.line);
- symbolRefs++;
- }
-
- StringPool::flattenUtf16(fileExportWriter.getBuffer(), symbolExportPool);
- return fileExportWriter;
-}
-
-} // namespace aapt
-
-#endif /* AAPT_FLATTEN_FILEEXPORTWRITER_H */
diff --git a/tools/aapt2/flatten/FileExportWriter_test.cpp b/tools/aapt2/flatten/FileExportWriter_test.cpp
deleted file mode 100644
index 32fc203..0000000
--- a/tools/aapt2/flatten/FileExportWriter_test.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Resource.h"
-
-#include "flatten/FileExportWriter.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
-#include "test/Common.h"
-
-#include <gtest/gtest.h>
-
-namespace aapt {
-
-TEST(FileExportWriterTest, FlattenResourceFileDataWithNoExports) {
- ResourceFile resFile = {
- test::parseNameOrDie(u"@android:layout/main.xml"),
- test::parseConfigOrDie("sw600dp-v4"),
- Source{ "res/layout/main.xml" },
- };
-
- BigBuffer buffer(1024);
- ChunkWriter writer = wrapBufferWithFileExportHeader(&buffer, &resFile);
- *writer.getBuffer()->nextBlock<uint32_t>() = 42u;
- writer.finish();
-
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-
- // There should be more data (string pool) besides the header and our data.
- ASSERT_GT(buffer.size(), sizeof(FileExport_header) + sizeof(uint32_t));
-
- // Write at the end of this chunk is our data.
- uint32_t* val = (uint32_t*)(data.get() + buffer.size()) - 1;
- EXPECT_EQ(*val, 42u);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h
index 02bff2c..3e20ad6 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -22,208 +22,6 @@
namespace aapt {
/**
- * New android::ResChunk_header types defined
- * for AAPT to use.
- *
- * TODO(adamlesinski): Consider reserving these
- * enums in androidfw/ResourceTypes.h to avoid
- * future collisions.
- */
-enum {
- /**
- * A chunk that contains an entire file that
- * has been compiled.
- */
- RES_FILE_EXPORT_TYPE = 0x000c,
-
- RES_TABLE_PUBLIC_TYPE = 0x000d,
-
- /**
- * A chunk that holds the string pool
- * for source entries (path/to/source:line).
- */
- RES_TABLE_SOURCE_POOL_TYPE = 0x000e,
-
- /**
- * A chunk holding names of externally
- * defined symbols and offsets to where
- * they are referenced in the table.
- */
- RES_TABLE_SYMBOL_TABLE_TYPE = 0x000f,
-};
-
-/**
- * New resource types that are meant to only be used
- * by AAPT and will not end up on the device.
- */
-struct ExtendedTypes {
- enum {
- /**
- * A raw string value that hasn't had its escape sequences
- * processed nor whitespace removed.
- */
- TYPE_RAW_STRING = 0xfe,
- };
-};
-
-/**
- * New types for a ResTable_map.
- */
-struct ExtendedResTableMapTypes {
- enum {
- /**
- * Type that contains the source path of the next item in the map.
- */
- ATTR_SOURCE_PATH = Res_MAKEINTERNAL(0xffff),
-
- /**
- * Type that contains the source line of the next item in the map.
- */
- ATTR_SOURCE_LINE = Res_MAKEINTERNAL(0xfffe),
-
- /**
- * Type that contains the comment of the next item in the map.
- */
- ATTR_COMMENT = Res_MAKEINTERNAL(0xfffd)
- };
-};
-
-/**
- * Followed by exportedSymbolCount ExportedSymbol structs, followed by the string pool.
- */
-struct FileExport_header {
- android::ResChunk_header header;
-
- /**
- * MAGIC value. Must be 'AAPT' (0x41415054)
- */
- uint8_t magic[4];
-
- /**
- * Version of AAPT that built this file.
- */
- uint32_t version;
-
- /**
- * The resource name.
- */
- android::ResStringPool_ref name;
-
- /**
- * Configuration of this file.
- */
- android::ResTable_config config;
-
- /**
- * Original source path of this file.
- */
- android::ResStringPool_ref source;
-
- /**
- * Number of symbols exported by this file.
- */
- uint32_t exportedSymbolCount;
-};
-
-struct ExportedSymbol {
- android::ResStringPool_ref name;
- uint32_t line;
-};
-
-struct Public_header {
- android::ResChunk_header header;
-
- /**
- * The ID of the type this structure refers to.
- */
- uint8_t typeId;
-
- /**
- * Reserved. Must be 0.
- */
- uint8_t res0;
-
- /**
- * Reserved. Must be 0.
- */
- uint16_t res1;
-
- /**
- * Number of public entries.
- */
- uint32_t count;
-};
-
-/**
- * A structure representing source data for a resource entry.
- * Appears after an android::ResTable_entry or android::ResTable_map_entry.
- *
- * TODO(adamlesinski): This causes some issues when runtime code checks
- * the size of an android::ResTable_entry. It assumes it is an
- * android::ResTable_map_entry if the size is bigger than an android::ResTable_entry
- * which may not be true if this structure is present.
- */
-struct ResTable_entry_source {
- /**
- * File path reference.
- */
- android::ResStringPool_ref path;
-
- /**
- * Line number this resource was defined on.
- */
- uint32_t line;
-
- /**
- * Comment string reference.
- */
- android::ResStringPool_ref comment;
-};
-
-struct Public_entry {
- uint16_t entryId;
-
- enum : uint16_t {
- kUndefined = 0,
- kPublic = 1,
- kPrivate = 2,
- };
-
- uint16_t state;
- android::ResStringPool_ref key;
- ResTable_entry_source source;
-};
-
-/**
- * A chunk with type RES_TABLE_SYMBOL_TABLE_TYPE.
- * Following the header are count number of SymbolTable_entry
- * structures, followed by an android::ResStringPool_header.
- */
-struct SymbolTable_header {
- android::ResChunk_header header;
-
- /**
- * Number of SymbolTable_entry structures following
- * this header.
- */
- uint32_t count;
-};
-
-struct SymbolTable_entry {
- /**
- * Offset from the beginning of the resource table
- * where the symbol entry is referenced.
- */
- uint32_t offset;
-
- /**
- * The index into the string pool where the name of this
- * symbol exists.
- */
- android::ResStringPool_ref name;
-};
-
-/**
* An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout
* struct.
*/
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 26d7c2c..71ab3db 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -51,157 +51,49 @@
dst[i] = 0;
}
+static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
+ if (a.key.id) {
+ if (b.key.id) {
+ return a.key.id.value() < b.key.id.value();
+ }
+ return true;
+ } else if (!b.key.id) {
+ return a.key.name.value() < b.key.name.value();
+ }
+ return false;
+}
+
struct FlatEntry {
ResourceEntry* entry;
Value* value;
// The entry string pool index to the entry's name.
uint32_t entryKey;
-
- // The source string pool index to the source file path.
- uint32_t sourcePathKey;
- uint32_t sourceLine;
-
- // The source string pool index to the comment.
- uint32_t commentKey;
};
-class SymbolWriter {
+class MapFlattenVisitor : public RawValueVisitor {
public:
- struct Entry {
- StringPool::Ref name;
- size_t offset;
- };
-
- std::vector<Entry> symbols;
-
- explicit SymbolWriter(StringPool* pool) : mPool(pool) {
- }
-
- void addSymbol(const Reference& ref, size_t offset) {
- const ResourceName& name = ref.name.value();
- std::u16string fullName;
- if (ref.privateReference) {
- fullName += u"*";
- }
-
- if (!name.package.empty()) {
- fullName += name.package + u":";
- }
- fullName += toString(name.type).toString() + u"/" + name.entry;
- symbols.push_back(Entry{ mPool->makeRef(fullName), offset });
- }
-
- void shiftAllOffsets(size_t offset) {
- for (Entry& entry : symbols) {
- entry.offset += offset;
- }
- }
-
-private:
- StringPool* mPool;
-};
-
-struct MapFlattenVisitor : public RawValueVisitor {
using RawValueVisitor::visit;
- SymbolWriter* mSymbols;
- FlatEntry* mEntry;
- BigBuffer* mBuffer;
- StringPool* mSourcePool;
- StringPool* mCommentPool;
- bool mUseExtendedChunks;
-
- size_t mEntryCount = 0;
- const Reference* mParent = nullptr;
-
- MapFlattenVisitor(SymbolWriter* symbols, FlatEntry* entry, BigBuffer* buffer,
- StringPool* sourcePool, StringPool* commentPool,
- bool useExtendedChunks) :
- mSymbols(symbols), mEntry(entry), mBuffer(buffer), mSourcePool(sourcePool),
- mCommentPool(commentPool), mUseExtendedChunks(useExtendedChunks) {
- }
-
- void flattenKey(Reference* key, ResTable_map* outEntry) {
- if (!key->id || (key->privateReference && mUseExtendedChunks)) {
- assert(key->name && "reference must have a name");
-
- outEntry->name.ident = util::hostToDevice32(0);
- mSymbols->addSymbol(*key, (mBuffer->size() - sizeof(ResTable_map)) +
- offsetof(ResTable_map, name));
- } else {
- outEntry->name.ident = util::hostToDevice32(key->id.value().id);
- }
- }
-
- void flattenValue(Item* value, ResTable_map* outEntry) {
- bool privateRef = false;
- if (Reference* ref = valueCast<Reference>(value)) {
- privateRef = ref->privateReference && mUseExtendedChunks;
- if (!ref->id || privateRef) {
- assert(ref->name && "reference must have a name");
-
- mSymbols->addSymbol(*ref, (mBuffer->size() - sizeof(ResTable_map)) +
- offsetof(ResTable_map, value) + offsetof(Res_value, data));
- }
- }
-
- bool result = value->flatten(&outEntry->value);
- if (privateRef) {
- outEntry->value.data = 0;
- }
- assert(result && "flatten failed");
- }
-
- void flattenEntry(Reference* key, Item* value) {
- ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
- flattenKey(key, outEntry);
- flattenValue(value, outEntry);
- outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
- mEntryCount++;
- }
-
- void flattenMetaData(Value* value) {
- if (!mUseExtendedChunks) {
- return;
- }
-
- Reference key(ResourceId{ ExtendedResTableMapTypes::ATTR_SOURCE_PATH });
- StringPool::Ref sourcePathRef = mSourcePool->makeRef(
- util::utf8ToUtf16(value->getSource().path));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC,
- static_cast<uint32_t>(sourcePathRef.getIndex()));
- flattenEntry(&key, &val);
-
- if (value->getSource().line) {
- key.id = ResourceId(ExtendedResTableMapTypes::ATTR_SOURCE_LINE);
- val.value.data = static_cast<uint32_t>(value->getSource().line.value());
- flattenEntry(&key, &val);
- }
-
- if (!value->getComment().empty()) {
- key.id = ResourceId(ExtendedResTableMapTypes::ATTR_COMMENT);
- StringPool::Ref commentRef = mCommentPool->makeRef(value->getComment());
- val.value.data = static_cast<uint32_t>(commentRef.getIndex());
- flattenEntry(&key, &val);
- }
+ MapFlattenVisitor(ResTable_entry_ext* outEntry, BigBuffer* buffer) :
+ mOutEntry(outEntry), mBuffer(buffer) {
}
void visit(Attribute* attr) override {
{
- Reference key(ResourceId{ ResTable_map::ATTR_TYPE });
+ Reference key = Reference(ResTable_map::ATTR_TYPE);
BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
flattenEntry(&key, &val);
}
if (attr->minInt != std::numeric_limits<int32_t>::min()) {
- Reference key(ResourceId{ ResTable_map::ATTR_MIN });
+ Reference key = Reference(ResTable_map::ATTR_MIN);
BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->minInt));
flattenEntry(&key, &val);
}
if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
- Reference key(ResourceId{ ResTable_map::ATTR_MAX });
+ Reference key = Reference(ResTable_map::ATTR_MAX);
BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->maxInt));
flattenEntry(&key, &val);
}
@@ -212,22 +104,11 @@
}
}
- static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
- if (a.key.id) {
- if (b.key.id) {
- return a.key.id.value() < b.key.id.value();
- }
- return true;
- } else if (!b.key.id) {
- return a.key.name.value() < b.key.name.value();
- }
- return false;
- }
-
void visit(Style* style) override {
if (style->parent) {
- // Parents are treated a bit differently, so record the existence and move on.
- mParent = &style->parent.value();
+ const Reference& parentRef = style->parent.value();
+ assert(parentRef.id && "parent has no ID");
+ mOutEntry->parent.ident = util::hostToDevice32(parentRef.id.value().id);
}
// Sort the style.
@@ -235,7 +116,6 @@
for (Style::Entry& entry : style->entries) {
flattenEntry(&entry.key, entry.value.get());
- flattenMetaData(&entry.key);
}
}
@@ -243,8 +123,8 @@
for (auto& attrRef : styleable->entries) {
BinaryPrimitive val(Res_value{});
flattenEntry(&attrRef, &val);
- flattenMetaData(&attrRef);
}
+
}
void visit(Array* array) override {
@@ -253,7 +133,6 @@
flattenValue(item.get(), outEntry);
outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
mEntryCount++;
- flattenMetaData(item.get());
}
}
@@ -297,18 +176,45 @@
Reference key(q);
flattenEntry(&key, plural->values[i].get());
- flattenMetaData(plural->values[i].get());
}
}
+
+ /**
+ * Call this after visiting a Value. This will finish any work that
+ * needs to be done to prepare the entry.
+ */
+ void finish() {
+ mOutEntry->count = util::hostToDevice32(mEntryCount);
+ }
+
+private:
+ void flattenKey(Reference* key, ResTable_map* outEntry) {
+ assert(key->id && "key has no ID");
+ outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+ }
+
+ void flattenValue(Item* value, ResTable_map* outEntry) {
+ bool result = value->flatten(&outEntry->value);
+ assert(result && "flatten failed");
+ }
+
+ void flattenEntry(Reference* key, Item* value) {
+ ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
+ flattenKey(key, outEntry);
+ flattenValue(value, outEntry);
+ outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+ mEntryCount++;
+ }
+
+ ResTable_entry_ext* mOutEntry;
+ BigBuffer* mBuffer;
+ size_t mEntryCount = 0;
};
class PackageFlattener {
public:
- PackageFlattener(IDiagnostics* diag, TableFlattenerOptions options,
- ResourceTablePackage* package, SymbolWriter* symbolWriter,
- StringPool* sourcePool) :
- mDiag(diag), mOptions(options), mPackage(package), mSymbols(symbolWriter),
- mSourcePool(sourcePool) {
+ PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package) :
+ mDiag(diag), mPackage(package) {
}
bool flattenPackage(BigBuffer* buffer) {
@@ -337,9 +243,6 @@
pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
StringPool::flattenUtf16(pkgWriter.getBuffer(), mKeyPool);
- // Add the ResTable_package header/type/key strings to the offset.
- mSymbols->shiftAllOffsets(pkgWriter.size());
-
// Append the types.
buffer->appendBuffer(std::move(typeBuffer));
@@ -349,12 +252,9 @@
private:
IDiagnostics* mDiag;
- TableFlattenerOptions mOptions;
ResourceTablePackage* mPackage;
StringPool mTypePool;
StringPool mKeyPool;
- SymbolWriter* mSymbols;
- StringPool* mSourcePool;
template <typename T, bool IsItem>
T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
@@ -376,62 +276,24 @@
outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
}
- outEntry->key.index = util::hostToDevice32(entry->entryKey);
- outEntry->size = sizeof(T);
-
- if (mOptions.useExtendedChunks) {
- // Write the extra source block. This will be ignored by the Android runtime.
- ResTable_entry_source* sourceBlock = buffer->nextBlock<ResTable_entry_source>();
- sourceBlock->path.index = util::hostToDevice32(entry->sourcePathKey);
- sourceBlock->line = util::hostToDevice32(entry->sourceLine);
- sourceBlock->comment.index = util::hostToDevice32(entry->commentKey);
- outEntry->size += sizeof(*sourceBlock);
- }
-
outEntry->flags = util::hostToDevice16(outEntry->flags);
- outEntry->size = util::hostToDevice16(outEntry->size);
+ outEntry->key.index = util::hostToDevice32(entry->entryKey);
+ outEntry->size = util::hostToDevice16(sizeof(T));
return result;
}
bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
if (Item* item = valueCast<Item>(entry->value)) {
writeEntry<ResTable_entry, true>(entry, buffer);
- bool privateRef = false;
- if (Reference* ref = valueCast<Reference>(entry->value)) {
- // If there is no ID or the reference is private and we allow extended chunks,
- // write out a 0 and mark the symbol table with the name of the reference.
- privateRef = (ref->privateReference && mOptions.useExtendedChunks);
- if (!ref->id || privateRef) {
- assert(ref->name && "reference must have at least a name");
- mSymbols->addSymbol(*ref, buffer->size() + offsetof(Res_value, data));
- }
- }
Res_value* outValue = buffer->nextBlock<Res_value>();
bool result = item->flatten(outValue);
assert(result && "flatten failed");
- if (privateRef) {
- // Force the value of 0 so we look up the symbol at unflatten time.
- outValue->data = 0;
- }
outValue->size = util::hostToDevice16(sizeof(*outValue));
} else {
- const size_t beforeEntry = buffer->size();
ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
- MapFlattenVisitor visitor(mSymbols, entry, buffer, mSourcePool, mSourcePool,
- mOptions.useExtendedChunks);
+ MapFlattenVisitor visitor(outEntry, buffer);
entry->value->accept(&visitor);
- outEntry->count = util::hostToDevice32(visitor.mEntryCount);
- if (visitor.mParent) {
- const bool forceSymbol = visitor.mParent->privateReference &&
- mOptions.useExtendedChunks;
- if (!visitor.mParent->id || forceSymbol) {
- assert(visitor.mParent->name && "reference must have a name");
- mSymbols->addSymbol(*visitor.mParent,
- beforeEntry + offsetof(ResTable_entry_ext, parent));
- } else {
- outEntry->parent.ident = util::hostToDevice32(visitor.mParent->id.value().id);
- }
- }
+ visitor.finish();
}
return true;
}
@@ -480,7 +342,7 @@
std::vector<ResourceTableType*> collectAndSortTypes() {
std::vector<ResourceTableType*> sortedTypes;
for (auto& type : mPackage->types) {
- if (type->type == ResourceType::kStyleable && !mOptions.useExtendedChunks) {
+ if (type->type == ResourceType::kStyleable) {
// Styleables aren't real Resource Types, they are represented in the R.java
// file.
continue;
@@ -551,52 +413,6 @@
return true;
}
- bool flattenPublic(ResourceTableType* type, std::vector<ResourceEntry*>* sortedEntries,
- BigBuffer* buffer) {
- ChunkWriter publicWriter(buffer);
- Public_header* publicHeader = publicWriter.startChunk<Public_header>(RES_TABLE_PUBLIC_TYPE);
- publicHeader->typeId = type->id.value();
-
- for (ResourceEntry* entry : *sortedEntries) {
- if (entry->symbolStatus.state != SymbolState::kUndefined) {
- // Write the public status of this entry.
- Public_entry* publicEntry = publicWriter.nextBlock<Public_entry>();
- publicEntry->entryId = util::hostToDevice32(entry->id.value());
- publicEntry->key.index = util::hostToDevice32(mKeyPool.makeRef(
- entry->name).getIndex());
- publicEntry->source.path.index = util::hostToDevice32(mSourcePool->makeRef(
- util::utf8ToUtf16(entry->symbolStatus.source.path)).getIndex());
- if (entry->symbolStatus.source.line) {
- publicEntry->source.line = util::hostToDevice32(
- entry->symbolStatus.source.line.value());
- }
- publicEntry->source.comment.index = util::hostToDevice32(mSourcePool->makeRef(
- entry->symbolStatus.comment).getIndex());
-
- switch (entry->symbolStatus.state) {
- case SymbolState::kPrivate:
- publicEntry->state = Public_entry::kPrivate;
- break;
-
- case SymbolState::kPublic:
- publicEntry->state = Public_entry::kPublic;
- break;
-
- case SymbolState::kUndefined:
- publicEntry->state = Public_entry::kUndefined;
- break;
- }
-
- // Don't hostToDevice until the last step.
- publicHeader->count += 1;
- }
- }
-
- publicHeader->count = util::hostToDevice32(publicHeader->count);
- publicWriter.finish();
- return true;
- }
-
bool flattenTypes(BigBuffer* buffer) {
// Sort the types by their IDs. They will be inserted into the StringPool in this order.
std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
@@ -620,12 +436,6 @@
return false;
}
- if (mOptions.useExtendedChunks) {
- if (!flattenPublic(type, &sortedEntries, buffer)) {
- return false;
- }
- }
-
// The binary resource table lists resource entries for each configuration.
// We store them inverted, where a resource entry lists the values for each
// configuration available. Here we reverse this to match the binary table.
@@ -635,26 +445,8 @@
// Group values by configuration.
for (auto& configValue : entry->values) {
- Value* value = configValue.value.get();
-
- const StringPool::Ref sourceRef = mSourcePool->makeRef(
- util::utf8ToUtf16(value->getSource().path));
-
- uint32_t lineNumber = 0;
- if (value->getSource().line) {
- lineNumber = value->getSource().line.value();
- }
-
- const StringPool::Ref commentRef = mSourcePool->makeRef(value->getComment());
-
- configToEntryListMap[configValue.config]
- .push_back(FlatEntry{
- entry,
- value,
- keyIndex,
- (uint32_t) sourceRef.getIndex(),
- lineNumber,
- (uint32_t) commentRef.getIndex() });
+ configToEntryListMap[configValue.config].push_back(FlatEntry{
+ entry, configValue.value.get(), keyIndex });
}
}
@@ -692,86 +484,18 @@
// Flatten the values string pool.
StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
- // If we have a reference to a symbol that doesn't exist, we don't know its resource ID.
- // We encode the name of the symbol along with the offset of where to include the resource ID
- // once it is found.
- StringPool symbolPool;
- std::vector<SymbolWriter::Entry> symbolOffsets;
-
- // String pool holding the source paths of each value.
- StringPool sourcePool;
-
BigBuffer packageBuffer(1024);
// Flatten each package.
for (auto& package : table->packages) {
- const size_t beforePackageSize = packageBuffer.size();
-
- // All packages will share a single global symbol pool.
- SymbolWriter packageSymbolWriter(&symbolPool);
-
- PackageFlattener flattener(context->getDiagnostics(), mOptions, package.get(),
- &packageSymbolWriter, &sourcePool);
+ PackageFlattener flattener(context->getDiagnostics(), package.get());
if (!flattener.flattenPackage(&packageBuffer)) {
return false;
}
-
- // The symbols are offset only from their own Package start. Offset them from the
- // start of the packageBuffer.
- packageSymbolWriter.shiftAllOffsets(beforePackageSize);
-
- // Extract all the symbols to offset
- symbolOffsets.insert(symbolOffsets.end(),
- std::make_move_iterator(packageSymbolWriter.symbols.begin()),
- std::make_move_iterator(packageSymbolWriter.symbols.end()));
}
- SymbolTable_entry* symbolEntryData = nullptr;
- if (mOptions.useExtendedChunks) {
- if (!symbolOffsets.empty()) {
- // Sort the offsets so we can scan them linearly.
- std::sort(symbolOffsets.begin(), symbolOffsets.end(),
- [](const SymbolWriter::Entry& a, const SymbolWriter::Entry& b) -> bool {
- return a.offset < b.offset;
- });
-
- // Write the Symbol header.
- ChunkWriter symbolWriter(tableWriter.getBuffer());
- SymbolTable_header* symbolHeader = symbolWriter.startChunk<SymbolTable_header>(
- RES_TABLE_SYMBOL_TABLE_TYPE);
- symbolHeader->count = util::hostToDevice32(symbolOffsets.size());
-
- symbolEntryData = symbolWriter.nextBlock<SymbolTable_entry>(symbolOffsets.size());
- StringPool::flattenUtf8(symbolWriter.getBuffer(), symbolPool);
- symbolWriter.finish();
- }
-
- if (sourcePool.size() > 0) {
- // Write out source pool.
- ChunkWriter srcWriter(tableWriter.getBuffer());
- srcWriter.startChunk<ResChunk_header>(RES_TABLE_SOURCE_POOL_TYPE);
- StringPool::flattenUtf8(srcWriter.getBuffer(), sourcePool);
- srcWriter.finish();
- }
- }
-
- const size_t beforePackagesSize = tableWriter.size();
-
// Finally merge all the packages into the main buffer.
tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
-
- // Update the offsets to their final values.
- if (symbolEntryData) {
- for (SymbolWriter::Entry& entry : symbolOffsets) {
- symbolEntryData->name.index = util::hostToDevice32(entry.name.getIndex());
-
- // The symbols were all calculated with the packageBuffer offset. We need to
- // add the beginning of the output buffer.
- symbolEntryData->offset = util::hostToDevice32(entry.offset + beforePackagesSize);
- symbolEntryData++;
- }
- }
-
tableWriter.finish();
return true;
}
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h
index 901b129..0ab0197 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/flatten/TableFlattener.h
@@ -24,28 +24,15 @@
class BigBuffer;
class ResourceTable;
-struct TableFlattenerOptions {
- /**
- * Specifies whether to output extended chunks, like
- * source information and missing symbol entries. Default
- * is false.
- *
- * Set this to true when emitting intermediate resource table.
- */
- bool useExtendedChunks = false;
-};
-
class TableFlattener : public IResourceTableConsumer {
public:
- TableFlattener(BigBuffer* buffer, TableFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
+ TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {
}
bool consume(IAaptContext* context, ResourceTable* table) override;
private:
BigBuffer* mBuffer;
- TableFlattenerOptions mOptions;
};
} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index 7030603..39c4fd3 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -38,9 +38,7 @@
::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
BigBuffer buffer(1024);
- TableFlattenerOptions options = {};
- options.useExtendedChunks = true;
- TableFlattener flattener(&buffer, options);
+ TableFlattener flattener(&buffer);
if (!flattener.consume(mContext.get(), table)) {
return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
@@ -54,9 +52,7 @@
::testing::AssertionResult flatten(ResourceTable* table, ResourceTable* outTable) {
BigBuffer buffer(1024);
- TableFlattenerOptions options = {};
- options.useExtendedChunks = true;
- TableFlattener flattener(&buffer, options);
+ TableFlattener flattener(&buffer);
if (!flattener.consume(mContext.get(), table)) {
return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
@@ -210,58 +206,6 @@
Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
}
-TEST_F(TableFlattenerTest, FlattenUnlinkedTable) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId(u"com.app.test", 0x7f)
- .addValue(u"@com.app.test:integer/one", ResourceId(0x7f020000),
- test::buildReference(u"@android:integer/foo"))
- .addValue(u"@com.app.test:style/Theme", ResourceId(0x7f030000), test::StyleBuilder()
- .setParent(u"@android:style/Theme.Material")
- .addItem(u"@android:attr/background", {})
- .addItem(u"@android:attr/colorAccent",
- test::buildReference(u"@com.app.test:color/green"))
- .build())
- .build();
-
- {
- // Need access to stringPool to make RawString.
- Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme");
- style->entries[0].value = util::make_unique<RawString>(table->stringPool.makeRef(u"foo"));
- }
-
- ResourceTable finalTable;
- ASSERT_TRUE(flatten(table.get(), &finalTable));
-
- Reference* ref = test::getValue<Reference>(&finalTable, u"@com.app.test:integer/one");
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@android:integer/foo"));
-
- Style* style = test::getValue<Style>(&finalTable, u"@com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().name);
- EXPECT_EQ(style->parent.value().name.value(),
- test::parseNameOrDie(u"@android:style/Theme.Material"));
-
- ASSERT_EQ(2u, style->entries.size());
-
- AAPT_ASSERT_TRUE(style->entries[0].key.name);
- EXPECT_EQ(style->entries[0].key.name.value(),
- test::parseNameOrDie(u"@android:attr/background"));
- RawString* raw = valueCast<RawString>(style->entries[0].value.get());
- ASSERT_NE(raw, nullptr);
- EXPECT_EQ(*raw->value, u"foo");
-
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::parseNameOrDie(u"@android:attr/colorAccent"));
- ref = valueCast<Reference>(style->entries[1].value.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@com.app.test:color/green"));
-}
-
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
Attribute attr(false);
attr.typeMask = android::ResTable_map::TYPE_INTEGER;
@@ -284,33 +228,4 @@
EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
}
-TEST_F(TableFlattenerTest, FlattenSourceAndCommentsForChildrenOfCompoundValues) {
- Style style;
- Reference key(test::parseNameOrDie(u"@android:attr/foo"));
- key.id = ResourceId(0x01010000);
- key.setSource(Source("test").withLine(2));
- key.setComment(StringPiece16(u"comment"));
- style.entries.push_back(Style::Entry{ key, util::make_unique<Id>() });
-
- test::ResourceTableBuilder builder = test::ResourceTableBuilder();
- std::unique_ptr<ResourceTable> table = builder
- .setPackageId(u"android", 0x01)
- .addValue(u"@android:attr/foo", ResourceId(0x01010000),
- test::AttributeBuilder().build())
- .addValue(u"@android:style/foo", ResourceId(0x01020000),
- std::unique_ptr<Style>(style.clone(builder.getStringPool())))
- .build();
-
- ResourceTable result;
- ASSERT_TRUE(flatten(table.get(), &result));
-
- Style* actualStyle = test::getValue<Style>(&result, u"@android:style/foo");
- ASSERT_NE(nullptr, actualStyle);
- ASSERT_EQ(1u, actualStyle->entries.size());
-
- Reference* actualKey = &actualStyle->entries[0].key;
- EXPECT_EQ(key.getSource(), actualKey->getSource());
- EXPECT_EQ(key.getComment(), actualKey->getComment());
-}
-
} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index fd76e88..8e32179 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -19,6 +19,7 @@
#include "Flags.h"
#include "Locale.h"
#include "NameMangler.h"
+#include "ResourceUtils.h"
#include "compile/IdAssigner.h"
#include "filter/ConfigFilter.h"
#include "flatten/Archive.h"
@@ -35,12 +36,14 @@
#include "link/TableMerger.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "proto/ProtoSerialize.h"
#include "unflatten/BinaryResourceParser.h"
-#include "unflatten/FileExportHeaderReader.h"
#include "util/Files.h"
#include "util/StringPiece.h"
#include "xml/XmlDom.h"
+#include <google/protobuf/io/coded_stream.h>
+
#include <fstream>
#include <sys/stat.h>
#include <vector>
@@ -144,6 +147,22 @@
return table;
}
+ std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
+ const void* data, size_t len) {
+ pb::ResourceTable pbTable;
+ if (!pbTable.ParseFromArray(data, len)) {
+ mContext->getDiagnostics()->error(DiagMessage(source) << "invalid compiled table");
+ return {};
+ }
+
+ std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source,
+ mContext->getDiagnostics());
+ if (!table) {
+ return {};
+ }
+ return table;
+ }
+
/**
* Inflates an XML file from the source path.
*/
@@ -161,18 +180,16 @@
const Source& source,
const void* data, size_t len,
IDiagnostics* diag) {
- std::string errorStr;
- ssize_t offset = getWrappedDataOffset(data, len, &errorStr);
- if (offset < 0) {
- diag->error(DiagMessage(source) << errorStr);
+ CompiledFileInputStream inputStream(data, len);
+ if (!inputStream.CompiledFile()) {
+ diag->error(DiagMessage(source) << "invalid compiled file header");
return {};
}
- std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(
- reinterpret_cast<const uint8_t*>(data) + static_cast<size_t>(offset),
- len - static_cast<size_t>(offset),
- diag,
- source);
+ const uint8_t* xmlData = reinterpret_cast<const uint8_t*>(inputStream.data());
+ const size_t xmlDataLen = inputStream.size();
+
+ std::unique_ptr<xml::XmlResource> xmlRes = xml::inflate(xmlData, xmlDataLen, diag, source);
if (!xmlRes) {
return {};
}
@@ -182,11 +199,16 @@
static std::unique_ptr<ResourceFile> loadFileExportHeader(const Source& source,
const void* data, size_t len,
IDiagnostics* diag) {
- std::unique_ptr<ResourceFile> resFile = util::make_unique<ResourceFile>();
- std::string errorStr;
- ssize_t offset = unwrapFileExportHeader(data, len, resFile.get(), &errorStr);
- if (offset < 0) {
- diag->error(DiagMessage(source) << errorStr);
+ CompiledFileInputStream inputStream(data, len);
+ const pb::CompiledFile* pbFile = inputStream.CompiledFile();
+ if (!pbFile) {
+ diag->error(DiagMessage(source) << "invalid compiled file header");
+ return {};
+ }
+
+ std::unique_ptr<ResourceFile> resFile = deserializeCompiledFileFromPb(*pbFile, source,
+ diag);
+ if (!resFile) {
return {};
}
return resFile;
@@ -214,16 +236,16 @@
return false;
}
- std::string errorStr;
- ssize_t offset = getWrappedDataOffset(data->data(), data->size(), &errorStr);
- if (offset < 0) {
- mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << errorStr);
+ CompiledFileInputStream inputStream(data->data(), data->size());
+ if (!inputStream.CompiledFile()) {
+ mContext->getDiagnostics()->error(DiagMessage(file->getSource())
+ << "invalid compiled file header");
return false;
}
if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
- if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
- data->size() - static_cast<size_t>(offset))) {
+ if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
+ inputStream.size())) {
if (writer->finishEntry()) {
return true;
}
@@ -307,9 +329,7 @@
bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
BigBuffer buffer(1024);
- TableFlattenerOptions options = {};
- options.useExtendedChunks = mOptions.staticLib;
- TableFlattener flattener(&buffer, options);
+ TableFlattener flattener(&buffer);
if (!flattener.consume(mContext, table)) {
return false;
}
@@ -445,8 +465,8 @@
return false;
}
- std::unique_ptr<ResourceTable> table = loadTable(file->getSource(), data->data(),
- data->size());
+ std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(), data->data(),
+ data->size());
if (!table) {
return false;
}
diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp
new file mode 100644
index 0000000..99981c5
--- /dev/null
+++ b/tools/aapt2/proto/ProtoHelpers.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#include "proto/ProtoHelpers.h"
+
+namespace aapt {
+
+void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool) {
+ BigBuffer buffer(1024);
+ StringPool::flattenUtf8(&buffer, pool);
+
+ std::string* data = outPbPool->mutable_data();
+ data->reserve(buffer.size());
+
+ size_t offset = 0;
+ for (const BigBuffer::Block& block : buffer) {
+ data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size);
+ offset += block.size;
+ }
+}
+
+void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource) {
+ StringPool::Ref ref = srcPool->makeRef(util::utf8ToUtf16(source.path));
+ outPbSource->set_path_idx(static_cast<uint32_t>(ref.getIndex()));
+ if (source.line) {
+ outPbSource->set_line_no(static_cast<uint32_t>(source.line.value()));
+ }
+}
+
+void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
+ Source* outSource) {
+ if (pbSource.has_path_idx()) {
+ outSource->path = util::getString8(srcPool, pbSource.path_idx()).toString();
+ }
+
+ if (pbSource.has_line_no()) {
+ outSource->line = static_cast<size_t>(pbSource.line_no());
+ }
+}
+
+pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state) {
+ switch (state) {
+ case SymbolState::kPrivate: return pb::SymbolStatus_Visibility_Private;
+ case SymbolState::kPublic: return pb::SymbolStatus_Visibility_Public;
+ default: break;
+ }
+ return pb::SymbolStatus_Visibility_Unknown;
+}
+
+SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility) {
+ switch (pbVisibility) {
+ case pb::SymbolStatus_Visibility_Private: return SymbolState::kPrivate;
+ case pb::SymbolStatus_Visibility_Public: return SymbolState::kPublic;
+ default: break;
+ }
+ return SymbolState::kUndefined;
+}
+
+void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig) {
+ android::ResTable_config flatConfig = config;
+ flatConfig.size = sizeof(flatConfig);
+ flatConfig.swapHtoD();
+ outPbConfig->set_data(&flatConfig, sizeof(flatConfig));
+}
+
+bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
+ ConfigDescription* outConfig) {
+ if (!pbConfig.has_data()) {
+ return false;
+ }
+
+ const android::ResTable_config* config;
+ if (pbConfig.data().size() > sizeof(*config)) {
+ return false;
+ }
+
+ config = reinterpret_cast<const android::ResTable_config*>(pbConfig.data().data());
+ outConfig->copyFromDtoH(*config);
+ return true;
+}
+
+pb::Reference_Type serializeReferenceTypeToPb(Reference::Type type) {
+ switch (type) {
+ case Reference::Type::kResource: return pb::Reference_Type_Ref;
+ case Reference::Type::kAttribute: return pb::Reference_Type_Attr;
+ default: break;
+ }
+ return pb::Reference_Type_Ref;
+}
+
+Reference::Type deserializeReferenceTypeFromPb(pb::Reference_Type pbType) {
+ switch (pbType) {
+ case pb::Reference_Type_Ref: return Reference::Type::kResource;
+ case pb::Reference_Type_Attr: return Reference::Type::kAttribute;
+ default: break;
+ }
+ return Reference::Type::kResource;
+}
+
+pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx) {
+ switch (pluralIdx) {
+ case Plural::Zero: return pb::Plural_Arity_Zero;
+ case Plural::One: return pb::Plural_Arity_One;
+ case Plural::Two: return pb::Plural_Arity_Two;
+ case Plural::Few: return pb::Plural_Arity_Few;
+ case Plural::Many: return pb::Plural_Arity_Many;
+ default: break;
+ }
+ return pb::Plural_Arity_Other;
+}
+
+size_t deserializePluralEnumFromPb(pb::Plural_Arity arity) {
+ switch (arity) {
+ case pb::Plural_Arity_Zero: return Plural::Zero;
+ case pb::Plural_Arity_One: return Plural::One;
+ case pb::Plural_Arity_Two: return Plural::Two;
+ case pb::Plural_Arity_Few: return Plural::Few;
+ case pb::Plural_Arity_Many: return Plural::Many;
+ default: break;
+ }
+ return Plural::Other;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
new file mode 100644
index 0000000..02e67f1
--- /dev/null
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_PROTO_PROTOHELPERS_H
+#define AAPT_PROTO_PROTOHELPERS_H
+
+#include "ConfigDescription.h"
+#include "ResourceTable.h"
+#include "Source.h"
+#include "StringPool.h"
+
+#include "proto/frameworks/base/tools/aapt2/Format.pb.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool);
+
+void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource);
+void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
+ Source* outSource);
+
+pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state);
+SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility);
+
+void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig);
+bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
+ ConfigDescription* outConfig);
+
+pb::Reference_Type serializeReferenceTypeToPb(Reference::Type type);
+Reference::Type deserializeReferenceTypeFromPb(pb::Reference_Type pbType);
+
+pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx);
+size_t deserializePluralEnumFromPb(pb::Plural_Arity arity);
+
+} // namespace aapt
+
+#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
new file mode 100644
index 0000000..6e224ab
--- /dev/null
+++ b/tools/aapt2/proto/ProtoSerialize.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
+#define AAPT_FLATTEN_TABLEPROTOSERIALIZER_H
+
+#include "Diagnostics.h"
+#include "ResourceTable.h"
+#include "Source.h"
+#include "proto/ProtoHelpers.h"
+
+#include <android-base/macros.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+
+namespace aapt {
+
+std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table);
+std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
+ const Source& source,
+ IDiagnostics* diag);
+
+std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file);
+std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
+ const Source& source,
+ IDiagnostics* diag);
+
+class CompiledFileOutputStream : public google::protobuf::io::CopyingOutputStream {
+public:
+ CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out,
+ pb::CompiledFile* pbFile);
+ bool Write(const void* data, int size) override;
+ bool Finish();
+
+private:
+ bool ensureFileWritten();
+
+ google::protobuf::io::CodedOutputStream mOut;
+ pb::CompiledFile* mPbFile;
+
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
+};
+
+class CompiledFileInputStream {
+public:
+ CompiledFileInputStream(const void* data, size_t size);
+
+ const pb::CompiledFile* CompiledFile();
+
+ const void* data();
+ size_t size();
+
+private:
+ google::protobuf::io::CodedInputStream mIn;
+ std::unique_ptr<pb::CompiledFile> mPbFile;
+ const uint8_t* mData;
+ size_t mSize;
+
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
new file mode 100644
index 0000000..1310aa6
--- /dev/null
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -0,0 +1,514 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "ResourceUtils.h"
+#include "ValueVisitor.h"
+#include "proto/ProtoHelpers.h"
+#include "proto/ProtoSerialize.h"
+#include "util/Comparators.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+namespace {
+
+class ReferenceIdToNameVisitor : public ValueVisitor {
+public:
+ using ValueVisitor::visit;
+
+ ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
+ mMapping(mapping) {
+ assert(mMapping);
+ }
+
+ void visit(Reference* reference) override {
+ if (!reference->id || !reference->id.value().isValid()) {
+ return;
+ }
+
+ ResourceId id = reference->id.value();
+ auto cacheIter = mMapping->find(id);
+ if (cacheIter != mMapping->end()) {
+ reference->name = cacheIter->second.toResourceName();
+ }
+ }
+
+private:
+ const std::map<ResourceId, ResourceNameRef>* mMapping;
+};
+
+class PackagePbDeserializer {
+public:
+ PackagePbDeserializer(const android::ResStringPool* valuePool,
+ const android::ResStringPool* sourcePool,
+ const android::ResStringPool* symbolPool,
+ const Source& source, IDiagnostics* diag) :
+ mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool),
+ mSource(source), mDiag(diag) {
+ }
+
+public:
+ bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
+ Maybe<uint8_t> id;
+ if (pbPackage.has_package_id()) {
+ id = static_cast<uint8_t>(pbPackage.package_id());
+ }
+
+ std::map<ResourceId, ResourceNameRef> idIndex;
+
+ ResourceTablePackage* pkg = table->createPackage(
+ util::utf8ToUtf16(pbPackage.package_name()), id);
+ for (const pb::Type& pbType : pbPackage.types()) {
+ const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name()));
+ if (!resType) {
+ mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
+ return {};
+ }
+
+ ResourceTableType* type = pkg->findOrCreateType(*resType);
+
+ for (const pb::Entry& pbEntry : pbType.entries()) {
+ ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name()));
+
+ // Deserialize the symbol status (public/private with source and comments).
+ if (pbEntry.has_symbol_status()) {
+ const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
+ if (pbStatus.has_source()) {
+ deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
+ &entry->symbolStatus.source);
+ }
+
+ if (pbStatus.has_comment()) {
+ entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment());
+ }
+
+ SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility());
+ entry->symbolStatus.state = visibility;
+
+ if (visibility == SymbolState::kPublic) {
+ // This is a public symbol, we must encode the ID now if there is one.
+ if (pbEntry.has_id()) {
+ entry->id = static_cast<uint16_t>(pbEntry.id());
+ }
+
+ if (type->symbolStatus.state != SymbolState::kPublic) {
+ // If the type has not been made public, do so now.
+ type->symbolStatus.state = SymbolState::kPublic;
+ if (pbType.has_id()) {
+ type->id = static_cast<uint8_t>(pbType.id());
+ }
+ }
+ } else if (visibility == SymbolState::kPrivate) {
+ if (type->symbolStatus.state == SymbolState::kUndefined) {
+ type->symbolStatus.state = SymbolState::kPrivate;
+ }
+ }
+ }
+
+ ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
+ if (resId.isValid()) {
+ idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
+ }
+
+ for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
+ const pb::ConfigDescription& pbConfig = pbConfigValue.config();
+
+ ConfigDescription config;
+ if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
+ mDiag->error(DiagMessage(mSource) << "invalid configuration");
+ return {};
+ }
+
+ auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
+ config, cmp::lessThanConfig);
+ if (iter != entry->values.end() && iter->config == config) {
+ // Duplicate config.
+ mDiag->error(DiagMessage(mSource) << "duplicate configuration");
+ return {};
+ }
+
+ std::unique_ptr<Value> value = deserializeValueFromPb(pbConfigValue.value(),
+ config,
+ &table->stringPool);
+ if (!value) {
+ return {};
+ }
+ entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) });
+ }
+ }
+ }
+
+ ReferenceIdToNameVisitor visitor(&idIndex);
+ visitAllValuesInPackage(pkg, &visitor);
+ return true;
+ }
+
+private:
+ std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
+ const ConfigDescription& config,
+ StringPool* pool) {
+ if (pbItem.has_ref()) {
+ const pb::Reference& pbRef = pbItem.ref();
+ std::unique_ptr<Reference> ref = util::make_unique<Reference>();
+ if (!deserializeReferenceFromPb(pbRef, ref.get())) {
+ return {};
+ }
+ return std::move(ref);
+
+ } else if (pbItem.has_prim()) {
+ const pb::Primitive& pbPrim = pbItem.prim();
+ android::Res_value prim = {};
+ prim.dataType = static_cast<uint8_t>(pbPrim.type());
+ prim.data = pbPrim.data();
+ return util::make_unique<BinaryPrimitive>(prim);
+
+ } else if (pbItem.has_id()) {
+ return util::make_unique<Id>();
+
+ } else if (pbItem.has_str()) {
+ const uint32_t idx = pbItem.str().idx();
+ StringPiece16 str = util::getString(*mValuePool, idx);
+
+ const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
+ if (spans && spans->name.index != android::ResStringPool_span::END) {
+ StyleString styleStr = { str.toString() };
+ while (spans->name.index != android::ResStringPool_span::END) {
+ styleStr.spans.push_back(Span{
+ util::getString(*mValuePool, spans->name.index).toString(),
+ spans->firstChar,
+ spans->lastChar
+ });
+ spans++;
+ }
+ return util::make_unique<StyledString>(
+ pool->makeRef(styleStr, StringPool::Context{ 1, config }));
+ }
+ return util::make_unique<String>(
+ pool->makeRef(str, StringPool::Context{ 1, config }));
+
+ } else if (pbItem.has_raw_str()) {
+ const uint32_t idx = pbItem.raw_str().idx();
+ StringPiece16 str = util::getString(*mValuePool, idx);
+ return util::make_unique<RawString>(
+ pool->makeRef(str, StringPool::Context{ 1, config }));
+
+ } else if (pbItem.has_file()) {
+ const uint32_t idx = pbItem.file().path_idx();
+ StringPiece16 str = util::getString(*mValuePool, idx);
+ return util::make_unique<FileReference>(
+ pool->makeRef(str, StringPool::Context{ 0, config }));
+
+ } else {
+ mDiag->error(DiagMessage(mSource) << "unknown item");
+ }
+ return {};
+ }
+
+ std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
+ const ConfigDescription& config,
+ StringPool* pool) {
+ const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
+
+ std::unique_ptr<Value> value;
+ if (pbValue.has_item()) {
+ value = deserializeItemFromPb(pbValue.item(), config, pool);
+ if (!value) {
+ return {};
+ }
+
+ } else if (pbValue.has_compound_value()) {
+ const pb::CompoundValue pbCompoundValue = pbValue.compound_value();
+ if (pbCompoundValue.has_attr()) {
+ const pb::Attribute& pbAttr = pbCompoundValue.attr();
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+ attr->typeMask = pbAttr.format_flags();
+ for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
+ Attribute::Symbol symbol;
+ deserializeItemCommon(pbSymbol, &symbol.symbol);
+ if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
+ return {};
+ }
+ symbol.value = pbSymbol.value();
+ attr->symbols.push_back(std::move(symbol));
+ }
+ value = std::move(attr);
+
+ } else if (pbCompoundValue.has_style()) {
+ const pb::Style& pbStyle = pbCompoundValue.style();
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+ if (pbStyle.has_parent()) {
+ style->parent = Reference();
+ if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
+ return {};
+ }
+
+ if (pbStyle.has_parent_source()) {
+ Source parentSource;
+ deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
+ &parentSource);
+ style->parent.value().setSource(std::move(parentSource));
+ }
+ }
+
+ for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
+ Style::Entry entry;
+ deserializeItemCommon(pbEntry, &entry.key);
+ if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
+ return {};
+ }
+
+ entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
+ if (!entry.value) {
+ return {};
+ }
+
+ deserializeItemCommon(pbEntry, entry.value.get());
+ style->entries.push_back(std::move(entry));
+ }
+ value = std::move(style);
+
+ } else if (pbCompoundValue.has_styleable()) {
+ const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
+ Reference attrRef;
+ deserializeItemCommon(pbEntry, &attrRef);
+ deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
+ styleable->entries.push_back(std::move(attrRef));
+ }
+ value = std::move(styleable);
+
+ } else if (pbCompoundValue.has_array()) {
+ const pb::Array& pbArray = pbCompoundValue.array();
+ std::unique_ptr<Array> array = util::make_unique<Array>();
+ for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
+ std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
+ pool);
+ if (!item) {
+ return {};
+ }
+
+ deserializeItemCommon(pbEntry, item.get());
+ array->items.push_back(std::move(item));
+ }
+ value = std::move(array);
+
+ } else if (pbCompoundValue.has_plural()) {
+ const pb::Plural& pbPlural = pbCompoundValue.plural();
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
+ size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
+ plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
+ pool);
+ if (!plural->values[pluralIdx]) {
+ return {};
+ }
+
+ deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
+ }
+ value = std::move(plural);
+
+ } else {
+ mDiag->error(DiagMessage(mSource) << "unknown compound value");
+ return {};
+ }
+ } else {
+ mDiag->error(DiagMessage(mSource) << "unknown value");
+ return {};
+ }
+
+ assert(value && "forgot to set value");
+
+ value->setWeak(isWeak);
+ deserializeItemCommon(pbValue, value.get());
+ return value;
+ }
+
+ bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
+ outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
+ outRef->privateReference = pbRef.private_();
+
+ if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
+ return false;
+ }
+
+ if (pbRef.has_id()) {
+ outRef->id = ResourceId(pbRef.id());
+ }
+
+ if (pbRef.has_symbol_idx()) {
+ StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
+ ResourceNameRef nameRef;
+ if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
+ mDiag->error(DiagMessage(mSource) << "invalid reference name '"
+ << strSymbol << "'");
+ return false;
+ }
+
+ outRef->name = nameRef.toResourceName();
+ }
+ return true;
+ }
+
+ template <typename T>
+ void deserializeItemCommon(const T& pbItem, Value* outValue) {
+ if (pbItem.has_source()) {
+ Source source;
+ deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
+ outValue->setSource(std::move(source));
+ }
+
+ if (pbItem.has_comment()) {
+ outValue->setComment(util::utf8ToUtf16(pbItem.comment()));
+ }
+ }
+
+private:
+ const android::ResStringPool* mValuePool;
+ const android::ResStringPool* mSourcePool;
+ const android::ResStringPool* mSymbolPool;
+ const Source mSource;
+ IDiagnostics* mDiag;
+};
+
+} // namespace
+
+std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
+ const Source& source,
+ IDiagnostics* diag) {
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+
+ if (!pbTable.has_string_pool()) {
+ diag->error(DiagMessage(source) << "no string pool found");
+ return {};
+ }
+
+ android::ResStringPool valuePool;
+ android::status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
+ pbTable.string_pool().data().size());
+ if (result != android::NO_ERROR) {
+ diag->error(DiagMessage(source) << "invalid string pool");
+ return {};
+ }
+
+ android::ResStringPool sourcePool;
+ if (pbTable.has_source_pool()) {
+ result = sourcePool.setTo(pbTable.source_pool().data().data(),
+ pbTable.source_pool().data().size());
+ if (result != android::NO_ERROR) {
+ diag->error(DiagMessage(source) << "invalid source pool");
+ return {};
+ }
+ }
+
+ android::ResStringPool symbolPool;
+ if (pbTable.has_symbol_pool()) {
+ result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
+ pbTable.symbol_pool().data().size());
+ if (result != android::NO_ERROR) {
+ diag->error(DiagMessage(source) << "invalid symbol pool");
+ return {};
+ }
+ }
+
+ PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
+ for (const pb::Package& pbPackage : pbTable.packages()) {
+ if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
+ return {};
+ }
+ }
+ return table;
+}
+
+std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
+ const Source& source,
+ IDiagnostics* diag) {
+ std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
+
+ ResourceNameRef nameRef;
+
+ // Need to create an lvalue here so that nameRef can point to something real.
+ std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name());
+ if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
+ diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
+ << pbFile.resource_name());
+ return {};
+ }
+ file->name = nameRef.toResourceName();
+ file->source.path = pbFile.source_path();
+ deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
+
+ for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
+ // Need to create an lvalue here so that nameRef can point to something real.
+ utf16Name = util::utf8ToUtf16(pbSymbol.resource_name());
+ if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
+ diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
+ "compiled file header: "
+ << pbFile.resource_name());
+ return {};
+ }
+ file->exportedSymbols.push_back(
+ SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
+ }
+ return file;
+}
+
+CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
+ mIn(static_cast<const uint8_t*>(data), size), mPbFile(),
+ mData(static_cast<const uint8_t*>(data)), mSize(size) {
+}
+
+const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
+ if (!mPbFile) {
+ std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
+ uint64_t pbSize = 0u;
+ if (!mIn.ReadLittleEndian64(&pbSize)) {
+ return nullptr;
+ }
+ mIn.PushLimit(static_cast<int>(pbSize));
+ if (!pbFile->ParsePartialFromCodedStream(&mIn)) {
+ return nullptr;
+ }
+
+ const size_t padding = 4 - (pbSize & 0x03);
+ mData += sizeof(uint64_t) + pbSize + padding;
+ mSize -= sizeof(uint64_t) + pbSize + padding;
+ mPbFile = std::move(pbFile);
+ }
+ return mPbFile.get();
+}
+
+const void* CompiledFileInputStream::data() {
+ if (!mPbFile) {
+ if (!CompiledFile()) {
+ return nullptr;
+ }
+ }
+ return mData;
+}
+
+size_t CompiledFileInputStream::size() {
+ if (!mPbFile) {
+ if (!CompiledFile()) {
+ return 0;
+ }
+ }
+ return mSize;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
new file mode 100644
index 0000000..4a2176d
--- /dev/null
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "StringPool.h"
+#include "ValueVisitor.h"
+#include "proto/ProtoHelpers.h"
+#include "proto/ProtoSerialize.h"
+#include "util/BigBuffer.h"
+
+namespace aapt {
+
+namespace {
+
+class PbSerializerVisitor : public RawValueVisitor {
+public:
+ using RawValueVisitor::visit;
+
+ /**
+ * Constructor to use when expecting to serialize any value.
+ */
+ PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Value* outPbValue) :
+ mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(outPbValue),
+ mOutPbItem(nullptr) {
+ }
+
+ /**
+ * Constructor to use when expecting to serialize an Item.
+ */
+ PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) :
+ mSourcePool(sourcePool), mSymbolPool(symbolPool), mOutPbValue(nullptr),
+ mOutPbItem(outPbItem) {
+ }
+
+ void visit(Reference* ref) override {
+ serializeReferenceToPb(*ref, getPbItem()->mutable_ref());
+ }
+
+ void visit(String* str) override {
+ getPbItem()->mutable_str()->set_idx(str->value.getIndex());
+ }
+
+ void visit(StyledString* str) override {
+ getPbItem()->mutable_str()->set_idx(str->value.getIndex());
+ }
+
+ void visit(FileReference* file) override {
+ getPbItem()->mutable_file()->set_path_idx(file->path.getIndex());
+ }
+
+ void visit(Id* id) override {
+ getPbItem()->mutable_id();
+ }
+
+ void visit(RawString* rawStr) override {
+ getPbItem()->mutable_raw_str()->set_idx(rawStr->value.getIndex());
+ }
+
+ void visit(BinaryPrimitive* prim) override {
+ android::Res_value val = {};
+ prim->flatten(&val);
+
+ pb::Primitive* pbPrim = getPbItem()->mutable_prim();
+ pbPrim->set_type(val.dataType);
+ pbPrim->set_data(val.data);
+ }
+
+ void visitItem(Item* item) override {
+ assert(false && "unimplemented item");
+ }
+
+ void visit(Attribute* attr) override {
+ pb::Attribute* pbAttr = getPbCompoundValue()->mutable_attr();
+ pbAttr->set_format_flags(attr->typeMask);
+ pbAttr->set_min_int(attr->minInt);
+ pbAttr->set_max_int(attr->maxInt);
+
+ for (auto& symbol : attr->symbols) {
+ pb::Attribute_Symbol* pbSymbol = pbAttr->add_symbols();
+ serializeItemCommonToPb(symbol.symbol, pbSymbol);
+ serializeReferenceToPb(symbol.symbol, pbSymbol->mutable_name());
+ pbSymbol->set_value(symbol.value);
+ }
+ }
+
+ void visit(Style* style) override {
+ pb::Style* pbStyle = getPbCompoundValue()->mutable_style();
+ if (style->parent) {
+ serializeReferenceToPb(style->parent.value(), pbStyle->mutable_parent());
+ serializeSourceToPb(style->parent.value().getSource(),
+ mSourcePool,
+ pbStyle->mutable_parent_source());
+ }
+
+ for (Style::Entry& entry : style->entries) {
+ pb::Style_Entry* pbEntry = pbStyle->add_entries();
+ serializeReferenceToPb(entry.key, pbEntry->mutable_key());
+
+ pb::Item* pbItem = pbEntry->mutable_item();
+ serializeItemCommonToPb(*entry.value, pbEntry);
+ PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbItem);
+ entry.value->accept(&subVisitor);
+ }
+ }
+
+ void visit(Styleable* styleable) override {
+ pb::Styleable* pbStyleable = getPbCompoundValue()->mutable_styleable();
+ for (Reference& entry : styleable->entries) {
+ pb::Styleable_Entry* pbEntry = pbStyleable->add_entries();
+ serializeItemCommonToPb(entry, pbEntry);
+ serializeReferenceToPb(entry, pbEntry->mutable_attr());
+ }
+ }
+
+ void visit(Array* array) override {
+ pb::Array* pbArray = getPbCompoundValue()->mutable_array();
+ for (auto& value : array->items) {
+ pb::Array_Entry* pbEntry = pbArray->add_entries();
+ serializeItemCommonToPb(*value, pbEntry);
+ PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbEntry->mutable_item());
+ value->accept(&subVisitor);
+ }
+ }
+
+ void visit(Plural* plural) override {
+ pb::Plural* pbPlural = getPbCompoundValue()->mutable_plural();
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ // No plural value set here.
+ continue;
+ }
+
+ pb::Plural_Entry* pbEntry = pbPlural->add_entries();
+ pbEntry->set_arity(serializePluralEnumToPb(i));
+ pb::Item* pbElement = pbEntry->mutable_item();
+ serializeItemCommonToPb(*plural->values[i], pbEntry);
+ PbSerializerVisitor subVisitor(mSourcePool, mSymbolPool, pbElement);
+ plural->values[i]->accept(&subVisitor);
+ }
+ }
+
+private:
+ pb::Item* getPbItem() {
+ if (mOutPbValue) {
+ return mOutPbValue->mutable_item();
+ }
+ return mOutPbItem;
+ }
+
+ pb::CompoundValue* getPbCompoundValue() {
+ assert(mOutPbValue);
+ return mOutPbValue->mutable_compound_value();
+ }
+
+ template <typename T>
+ void serializeItemCommonToPb(const Item& item, T* pbItem) {
+ serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source());
+ if (!item.getComment().empty()) {
+ pbItem->set_comment(util::utf16ToUtf8(item.getComment()));
+ }
+ }
+
+ void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) {
+ if (ref.id) {
+ pbRef->set_id(ref.id.value().id);
+ } else if (ref.name) {
+ StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString());
+ pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex()));
+ }
+ pbRef->set_private_(ref.privateReference);
+ pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType));
+ }
+
+ StringPool* mSourcePool;
+ StringPool* mSymbolPool;
+ pb::Value* mOutPbValue;
+ pb::Item* mOutPbItem;
+};
+
+} // namespace
+
+std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) {
+ // We must do this before writing the resources, since the string pool IDs may change.
+ table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ int diff = a.context.priority - b.context.priority;
+ if (diff < 0) return true;
+ if (diff > 0) return false;
+ diff = a.context.config.compare(b.context.config);
+ if (diff < 0) return true;
+ if (diff > 0) return false;
+ return a.value < b.value;
+ });
+ table->stringPool.prune();
+
+ std::unique_ptr<pb::ResourceTable> pbTable = util::make_unique<pb::ResourceTable>();
+ serializeStringPoolToPb(table->stringPool, pbTable->mutable_string_pool());
+
+ StringPool sourcePool, symbolPool;
+
+ for (auto& package : table->packages) {
+ pb::Package* pbPackage = pbTable->add_packages();
+ if (package->id) {
+ pbPackage->set_package_id(package->id.value());
+ }
+ pbPackage->set_package_name(util::utf16ToUtf8(package->name));
+
+ for (auto& type : package->types) {
+ pb::Type* pbType = pbPackage->add_types();
+ if (type->id) {
+ pbType->set_id(type->id.value());
+ }
+ pbType->set_name(util::utf16ToUtf8(toString(type->type)));
+
+ for (auto& entry : type->entries) {
+ pb::Entry* pbEntry = pbType->add_entries();
+ if (entry->id) {
+ pbEntry->set_id(entry->id.value());
+ }
+ pbEntry->set_name(util::utf16ToUtf8(entry->name));
+
+ // Write the SymbolStatus struct.
+ pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status();
+ pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state));
+ serializeSourceToPb(entry->symbolStatus.source, &sourcePool,
+ pbStatus->mutable_source());
+ pbStatus->set_comment(util::utf16ToUtf8(entry->symbolStatus.comment));
+
+ for (auto& configValue : entry->values) {
+ pb::ConfigValue* pbConfigValue = pbEntry->add_config_values();
+ serializeConfig(configValue.config, pbConfigValue->mutable_config());
+
+ pb::Value* pbValue = pbConfigValue->mutable_value();
+ serializeSourceToPb(configValue.value->getSource(), &sourcePool,
+ pbValue->mutable_source());
+ if (!configValue.value->getComment().empty()) {
+ pbValue->set_comment(util::utf16ToUtf8(configValue.value->getComment()));
+ }
+
+ if (configValue.value->isWeak()) {
+ pbValue->set_weak(true);
+ }
+
+ PbSerializerVisitor visitor(&sourcePool, &symbolPool, pbValue);
+ configValue.value->accept(&visitor);
+ }
+ }
+ }
+ }
+
+ serializeStringPoolToPb(sourcePool, pbTable->mutable_source_pool());
+ serializeStringPoolToPb(symbolPool, pbTable->mutable_symbol_pool());
+ return pbTable;
+}
+
+std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) {
+ std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
+ pbFile->set_resource_name(util::utf16ToUtf8(file.name.toString()));
+ pbFile->set_source_path(file.source.path);
+ serializeConfig(file.config, pbFile->mutable_config());
+
+ for (const SourcedResourceName& exported : file.exportedSymbols) {
+ pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols();
+ pbSymbol->set_resource_name(util::utf16ToUtf8(exported.name.toString()));
+ pbSymbol->set_line_no(exported.line);
+ }
+ return pbFile;
+}
+
+CompiledFileOutputStream::CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out,
+ pb::CompiledFile* pbFile) :
+ mOut(out), mPbFile(pbFile) {
+}
+
+bool CompiledFileOutputStream::ensureFileWritten() {
+ if (mPbFile) {
+ const uint64_t pbSize = mPbFile->ByteSize();
+ mOut.WriteLittleEndian64(pbSize);
+ mPbFile->SerializeWithCachedSizes(&mOut);
+ const size_t padding = 4 - (pbSize & 0x03);
+ if (padding > 0) {
+ uint32_t zero = 0u;
+ mOut.WriteRaw(&zero, padding);
+ }
+ mPbFile = nullptr;
+ }
+ return !mOut.HadError();
+}
+
+bool CompiledFileOutputStream::Write(const void* data, int size) {
+ if (!ensureFileWritten()) {
+ return false;
+ }
+ mOut.WriteRaw(data, size);
+ return !mOut.HadError();
+}
+
+bool CompiledFileOutputStream::Finish() {
+ return ensureFileWritten();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
new file mode 100644
index 0000000..1061b8f
--- /dev/null
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "proto/ProtoSerialize.h"
+#include "test/Builders.h"
+#include "test/Common.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(TableProtoSerializer, SerializeSinglePackage) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"com.app.a", 0x7f)
+ .addFileReference(u"@com.app.a:layout/main", ResourceId(0x7f020000),
+ u"res/layout/main.xml")
+ .addReference(u"@com.app.a:layout/other", ResourceId(0x7f020001),
+ u"@com.app.a:layout/main")
+ .addString(u"@com.app.a:string/text", {}, u"hi")
+ .addValue(u"@com.app.a:id/foo", {}, util::make_unique<Id>())
+ .build();
+
+ Symbol publicSymbol;
+ publicSymbol.state = SymbolState::kPublic;
+ ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@com.app.a:layout/main"),
+ ResourceId(0x7f020000),
+ publicSymbol, context->getDiagnostics()));
+
+ Id* id = test::getValue<Id>(table.get(), u"@com.app.a:id/foo");
+ ASSERT_NE(nullptr, id);
+
+ // Make a plural.
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one"));
+ ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"),
+ ConfigDescription{}, std::move(plural),
+ context->getDiagnostics()));
+
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
+ ASSERT_NE(nullptr, pbTable);
+
+ std::unique_ptr<ResourceTable> newTable = deserializeTableFromPb(*pbTable,
+ Source{ "test" },
+ context->getDiagnostics());
+ ASSERT_NE(nullptr, newTable);
+
+ Id* newId = test::getValue<Id>(newTable.get(), u"@com.app.a:id/foo");
+ ASSERT_NE(nullptr, newId);
+ EXPECT_EQ(id->isWeak(), newId->isWeak());
+
+ Maybe<ResourceTable::SearchResult> result = newTable->findResource(
+ test::parseNameOrDie(u"@com.app.a:layout/main"));
+ AAPT_ASSERT_TRUE(result);
+ EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state);
+ EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+}
+
+TEST(TableProtoSerializer, SerializeFileHeader) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+
+ ResourceFile f;
+ f.config = test::parseConfigOrDie("hdpi-v9");
+ f.name = test::parseNameOrDie(u"@com.app.a:layout/main");
+ f.source.path = "res/layout-hdpi-v9/main.xml";
+ f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie(u"@+id/unchecked"), 23u });
+
+ const std::string expectedData = "1234";
+
+ std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f);
+
+ std::string outputStr;
+ {
+ google::protobuf::io::StringOutputStream outStream(&outputStr);
+ CompiledFileOutputStream outFileStream(&outStream, pbFile.get());
+
+ ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size()));
+ ASSERT_TRUE(outFileStream.Finish());
+ }
+
+ CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size());
+ const pb::CompiledFile* newPbFile = inFileStream.CompiledFile();
+ ASSERT_NE(nullptr, newPbFile);
+
+ std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(*newPbFile, Source{ "test" },
+ context->getDiagnostics());
+ ASSERT_NE(nullptr, file);
+
+ std::string actualData((const char*)inFileStream.data(), inFileStream.size());
+ EXPECT_EQ(expectedData, actualData);
+ EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(inFileStream.data()) & 0x03);
+
+ ASSERT_EQ(1u, file->exportedSymbols.size());
+ EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 6b7a63cf..3417703 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -19,8 +19,6 @@
#include "ResourceValues.h"
#include "Source.h"
#include "ValueVisitor.h"
-
-#include "flatten/ResourceTypeExtensions.h"
#include "unflatten/BinaryResourceParser.h"
#include "unflatten/ResChunkPullParser.h"
#include "util/Util.h"
@@ -36,6 +34,8 @@
using namespace android;
+namespace {
+
/*
* Visitor that converts a reference's resource ID to a resource name,
* given a mapping from resource ID to resource name.
@@ -66,6 +66,8 @@
}
};
+} // namespace
+
BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table,
const Source& source, const void* data, size_t len) :
mContext(context), mTable(table), mSource(source), mData(data), mDataLen(len) {
@@ -97,106 +99,6 @@
return !error;
}
-Maybe<Reference> BinaryResourceParser::getSymbol(const void* data) {
- if (!mSymbolEntries || mSymbolEntryCount == 0) {
- return {};
- }
-
- if ((uintptr_t) data < (uintptr_t) mData) {
- return {};
- }
-
- // We only support 32 bit offsets right now.
- const uintptr_t offset = (uintptr_t) data - (uintptr_t) mData;
- if (offset > std::numeric_limits<uint32_t>::max()) {
- return {};
- }
-
- for (size_t i = 0; i < mSymbolEntryCount; i++) {
- if (util::deviceToHost32(mSymbolEntries[i].offset) == offset) {
- // This offset is a symbol!
- const StringPiece16 str = util::getString(
- mSymbolPool, util::deviceToHost32(mSymbolEntries[i].name.index));
-
- ResourceNameRef nameRef;
- bool privateRef = false;
- if (!ResourceUtils::parseResourceName(str, &nameRef, &privateRef)) {
- return {};
- }
-
- // Since we scan the symbol table in order, we can start looking for the
- // next symbol from this point.
- mSymbolEntryCount -= i + 1;
- mSymbolEntries += i + 1;
-
- Reference ref(nameRef);
- ref.privateReference = privateRef;
- return Maybe<Reference>(std::move(ref));
- }
- }
- return {};
-}
-
-/**
- * Parses the SymbolTable_header, which is present on non-final resource tables
- * after the compile phase.
- *
- * | SymbolTable_header |
- * |--------------------|
- * |SymbolTable_entry 0 |
- * |SymbolTable_entry 1 |
- * | ... |
- * |SymbolTable_entry n |
- * |--------------------|
- *
- */
-bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
- const SymbolTable_header* header = convertTo<SymbolTable_header>(chunk);
- if (!header) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt SymbolTable_header");
- return false;
- }
-
- const uint32_t entrySizeBytes =
- util::deviceToHost32(header->count) * sizeof(SymbolTable_entry);
- if (entrySizeBytes > getChunkDataLen(&header->header)) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "SymbolTable_header data section too long");
- return false;
- }
-
- mSymbolEntries = (const SymbolTable_entry*) getChunkData(&header->header);
- mSymbolEntryCount = util::deviceToHost32(header->count);
-
- // Skip over the symbol entries and parse the StringPool chunk that should be next.
- ResChunkPullParser parser(getChunkData(&header->header) + entrySizeBytes,
- getChunkDataLen(&header->header) - entrySizeBytes);
- if (!ResChunkPullParser::isGoodEvent(parser.next())) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "failed to parse chunk in SymbolTable: "
- << parser.getLastError());
- return false;
- }
-
- const ResChunk_header* nextChunk = parser.getChunk();
- if (util::deviceToHost16(nextChunk->type) != android::RES_STRING_POOL_TYPE) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "expected string pool in SymbolTable but got "
- << "chunk of type "
- << (int) util::deviceToHost16(nextChunk->type));
- return false;
- }
-
- if (mSymbolPool.setTo(nextChunk, util::deviceToHost32(nextChunk->size)) != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt string pool in SymbolTable: "
- << mSymbolPool.getError());
- return false;
- }
- return true;
-}
-
/**
* Parses the resource table, which contains all the packages, types, and entries.
*/
@@ -230,24 +132,6 @@
}
break;
- case RES_TABLE_SYMBOL_TABLE_TYPE:
- if (!parseSymbolTable(parser.getChunk())) {
- return false;
- }
- break;
-
- case RES_TABLE_SOURCE_POOL_TYPE: {
- status_t err = mSourcePool.setTo(getChunkData(parser.getChunk()),
- getChunkDataLen(parser.getChunk()));
- if (err != NO_ERROR) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt source string pool in ResTable: "
- << mSourcePool.getError());
- return false;
- }
- break;
- }
-
case android::RES_TABLE_PACKAGE_TYPE:
if (!parsePackage(parser.getChunk())) {
return false;
@@ -350,12 +234,6 @@
}
break;
- case RES_TABLE_PUBLIC_TYPE:
- if (!parsePublic(package, parser.getChunk())) {
- return false;
- }
- break;
-
default:
mContext->getDiagnostics()
->warn(DiagMessage(mSource)
@@ -375,97 +253,7 @@
// Now go through the table and change local resource ID references to
// symbolic references.
ReferenceIdToNameVisitor visitor(&mIdIndex);
- for (auto& package : mTable->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- for (auto& configValue : entry->values) {
- configValue.value->accept(&visitor);
- }
- }
- }
- }
- return true;
-}
-
-bool BinaryResourceParser::parsePublic(const ResourceTablePackage* package,
- const ResChunk_header* chunk) {
- const Public_header* header = convertTo<Public_header>(chunk);
- if (!header) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "corrupt Public_header chunk");
- return false;
- }
-
- if (header->typeId == 0) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "invalid type ID "
- << (int) header->typeId);
- return false;
- }
-
- StringPiece16 typeStr16 = util::getString(mTypePool, header->typeId - 1);
- const ResourceType* parsedType = parseResourceType(typeStr16);
- if (!parsedType) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "invalid type '" << typeStr16 << "'");
- return false;
- }
-
- const uintptr_t chunkEnd = (uintptr_t) chunk + util::deviceToHost32(chunk->size);
- const Public_entry* entry = (const Public_entry*) getChunkData(&header->header);
- for (uint32_t i = 0; i < util::deviceToHost32(header->count); i++) {
- if ((uintptr_t) entry + sizeof(*entry) > chunkEnd) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "Public_entry data section is too long");
- return false;
- }
-
- const ResourceId resId(package->id.value(), header->typeId,
- util::deviceToHost16(entry->entryId));
-
- const ResourceName name(package->name, *parsedType,
- util::getString(mKeyPool, entry->key.index).toString());
-
- Symbol symbol;
- if (mSourcePool.getError() == NO_ERROR) {
- symbol.source.path = util::utf16ToUtf8(util::getString(
- mSourcePool, util::deviceToHost32(entry->source.path.index)));
- symbol.source.line = util::deviceToHost32(entry->source.line);
- }
-
- StringPiece16 comment = util::getString(mSourcePool,
- util::deviceToHost32(entry->source.comment.index));
- if (!comment.empty()) {
- symbol.comment = comment.toString();
- }
-
- switch (util::deviceToHost16(entry->state)) {
- case Public_entry::kPrivate:
- symbol.state = SymbolState::kPrivate;
- break;
-
- case Public_entry::kPublic:
- symbol.state = SymbolState::kPublic;
- break;
-
- case Public_entry::kUndefined:
- symbol.state = SymbolState::kUndefined;
- break;
- }
-
- if (!mTable->setSymbolStateAllowMangled(name, resId, symbol, mContext->getDiagnostics())) {
- return false;
- }
-
- // Add this resource name->id mapping to the index so
- // that we can resolve all ID references to name references.
- auto cacheIter = mIdIndex.find(resId);
- if (cacheIter == mIdIndex.end()) {
- mIdIndex.insert({ resId, name });
- }
-
- entry++;
- }
+ visitAllValuesInTable(mTable, &visitor);
return true;
}
@@ -545,25 +333,12 @@
const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index()));
std::unique_ptr<Value> resourceValue;
- const ResTable_entry_source* sourceBlock = nullptr;
-
if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
- if (util::deviceToHost32(mapEntry->size) - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
- const uint8_t* data = (const uint8_t*) mapEntry;
- data += util::deviceToHost32(mapEntry->size) - sizeof(*sourceBlock);
- sourceBlock = (const ResTable_entry_source*) data;
- }
// TODO(adamlesinski): Check that the entry count is valid.
resourceValue = parseMapEntry(name, config, mapEntry);
} else {
- if (util::deviceToHost32(entry->size) - sizeof(*entry) == sizeof(*sourceBlock)) {
- const uint8_t* data = (const uint8_t*) entry;
- data += util::deviceToHost32(entry->size) - sizeof(*sourceBlock);
- sourceBlock = (const ResTable_entry_source*) data;
- }
-
const Res_value* value = (const Res_value*)(
(const uint8_t*) entry + util::deviceToHost32(entry->size));
resourceValue = parseValue(name, config, value, entry->flags);
@@ -577,30 +352,6 @@
return false;
}
- Source source = mSource;
- if (sourceBlock) {
- StringPiece path = util::getString8(mSourcePool,
- util::deviceToHost32(sourceBlock->path.index));
- if (!path.empty()) {
- source.path = path.toString();
- }
- source.line = util::deviceToHost32(sourceBlock->line);
-
- if (Style* style = valueCast<Style>(resourceValue.get())) {
- // The parent's source is the same as the resource itself, set it here.
- if (style->parent) {
- style->parent.value().setSource(source);
- }
- }
- }
-
- StringPiece16 comment = util::getString(mSourcePool,
- util::deviceToHost32(sourceBlock->comment.index));
- if (!comment.empty()) {
- resourceValue->setComment(comment);
- }
-
- resourceValue->setSource(source);
if (!mTable->addResourceAllowMangled(name, config, std::move(resourceValue),
mContext->getDiagnostics())) {
return false;
@@ -674,26 +425,15 @@
const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
Reference::Type::kResource : Reference::Type::kAttribute;
- if (data != 0) {
- // This is a normal reference.
- return util::make_unique<Reference>(data, type);
+ if (data == 0) {
+ // A reference of 0, must be the magic @null reference.
+ Res_value nullType = {};
+ nullType.dataType = Res_value::TYPE_REFERENCE;
+ return util::make_unique<BinaryPrimitive>(nullType);
}
- // This reference has an invalid ID. Check if it is an unresolved symbol.
- if (Maybe<Reference> ref = getSymbol(&value->data)) {
- ref.value().referenceType = type;
- return util::make_unique<Reference>(std::move(ref.value()));
- }
-
- // This is not an unresolved symbol, so it must be the magic @null reference.
- Res_value nullType = {};
- nullType.dataType = Res_value::TYPE_REFERENCE;
- return util::make_unique<BinaryPrimitive>(nullType);
- }
-
- if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
- return util::make_unique<RawString>(mTable->stringPool.makeRef(
- util::getString(mValuePool, data), StringPool::Context{ 1, config }));
+ // This is a normal reference.
+ return util::make_unique<Reference>(data, type);
}
// Treat this as a raw binary primitive.
@@ -712,8 +452,6 @@
return parseAttr(name, config, map);
case ResourceType::kArray:
return parseArray(name, config, map);
- case ResourceType::kStyleable:
- return parseStyleable(name, config, map);
case ResourceType::kPlurals:
return parsePlural(name, config, map);
default:
@@ -727,51 +465,23 @@
const ConfigDescription& config,
const ResTable_map_entry* map) {
std::unique_ptr<Style> style = util::make_unique<Style>();
- if (util::deviceToHost32(map->parent.ident) == 0) {
- // The parent is either not set or it is an unresolved symbol.
- // Check to see if it is a symbol.
- style->parent = getSymbol(&map->parent.ident);
-
- } else {
- // The parent is a regular reference to a resource.
+ if (util::deviceToHost32(map->parent.ident) != 0) {
+ // The parent is a regular reference to a resource.
style->parent = Reference(util::deviceToHost32(map->parent.ident));
}
for (const ResTable_map& mapEntry : map) {
if (Res_INTERNALID(util::deviceToHost32(mapEntry.name.ident))) {
- if (style->entries.empty()) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "out-of-sequence meta data in style");
- return {};
- }
- collectMetaData(mapEntry, &style->entries.back().key);
continue;
}
- style->entries.emplace_back();
- Style::Entry& styleEntry = style->entries.back();
-
- if (util::deviceToHost32(mapEntry.name.ident) == 0) {
- // The map entry's key (attribute) is not set. This must be
- // a symbol reference, so resolve it.
- Maybe<Reference> symbol = getSymbol(&mapEntry.name.ident);
- if (!symbol) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "unresolved style attribute");
- return {};
- }
- styleEntry.key = std::move(symbol.value());
-
- } else {
- // The map entry's key (attribute) is a regular reference.
- styleEntry.key.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
- }
-
- // Parse the attribute's value.
+ Style::Entry styleEntry;
+ styleEntry.key = Reference(util::deviceToHost32(mapEntry.name.ident));
styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
if (!styleEntry.value) {
return {};
}
+ style->entries.push_back(std::move(styleEntry));
}
return style;
}
@@ -807,22 +517,7 @@
if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
Attribute::Symbol symbol;
symbol.value = util::deviceToHost32(mapEntry.value.data);
- if (util::deviceToHost32(mapEntry.name.ident) == 0) {
- // The map entry's key (id) is not set. This must be
- // a symbol reference, so resolve it.
- Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
- if (!ref) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "unresolved attribute symbol");
- return {};
- }
- symbol.symbol = std::move(ref.value());
-
- } else {
- // The map entry's key (id) is a regular reference.
- symbol.symbol.id = ResourceId(util::deviceToHost32(mapEntry.name.ident));
- }
-
+ symbol.symbol = Reference(util::deviceToHost32(mapEntry.name.ident));
attr->symbols.push_back(std::move(symbol));
}
}
@@ -831,115 +526,26 @@
return attr;
}
-static bool isMetaDataEntry(const ResTable_map& mapEntry) {
- switch (util::deviceToHost32(mapEntry.name.ident)) {
- case ExtendedResTableMapTypes::ATTR_SOURCE_PATH:
- case ExtendedResTableMapTypes::ATTR_SOURCE_LINE:
- case ExtendedResTableMapTypes::ATTR_COMMENT:
- return true;
- }
- return false;
-}
-
-bool BinaryResourceParser::collectMetaData(const ResTable_map& mapEntry, Value* value) {
- switch (util::deviceToHost32(mapEntry.name.ident)) {
- case ExtendedResTableMapTypes::ATTR_SOURCE_PATH:
- value->setSource(Source(util::getString8(mSourcePool,
- util::deviceToHost32(mapEntry.value.data))));
- return true;
- break;
-
- case ExtendedResTableMapTypes::ATTR_SOURCE_LINE:
- value->setSource(value->getSource().withLine(util::deviceToHost32(mapEntry.value.data)));
- return true;
- break;
-
- case ExtendedResTableMapTypes::ATTR_COMMENT:
- value->setComment(util::getString(mSourcePool, util::deviceToHost32(mapEntry.value.data)));
- return true;
- break;
- }
- return false;
-}
-
std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
const ConfigDescription& config,
const ResTable_map_entry* map) {
std::unique_ptr<Array> array = util::make_unique<Array>();
- Source source;
for (const ResTable_map& mapEntry : map) {
- if (isMetaDataEntry(mapEntry)) {
- if (array->items.empty()) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "out-of-sequence meta data in array");
- return {};
- }
- collectMetaData(mapEntry, array->items.back().get());
- continue;
- }
-
array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
}
return array;
}
-std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
- const ConfigDescription& config,
- const ResTable_map_entry* map) {
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
- for (const ResTable_map& mapEntry : map) {
- if (isMetaDataEntry(mapEntry)) {
- if (styleable->entries.empty()) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "out-of-sequence meta data in styleable");
- return {};
- }
- collectMetaData(mapEntry, &styleable->entries.back());
- continue;
- }
-
- if (util::deviceToHost32(mapEntry.name.ident) == 0) {
- // The map entry's key (attribute) is not set. This must be
- // a symbol reference, so resolve it.
- Maybe<Reference> ref = getSymbol(&mapEntry.name.ident);
- if (!ref) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "unresolved styleable symbol");
- return {};
- }
- styleable->entries.emplace_back(std::move(ref.value()));
-
- } else {
- // The map entry's key (attribute) is a regular reference.
- styleable->entries.emplace_back(util::deviceToHost32(mapEntry.name.ident));
- }
- }
- return styleable;
-}
-
std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
const ConfigDescription& config,
const ResTable_map_entry* map) {
std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- Item* lastEntry = nullptr;
for (const ResTable_map& mapEntry : map) {
- if (isMetaDataEntry(mapEntry)) {
- if (!lastEntry) {
- mContext->getDiagnostics()->error(DiagMessage(mSource)
- << "out-of-sequence meta data in plural");
- return {};
- }
- collectMetaData(mapEntry, lastEntry);
- continue;
- }
-
std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
if (!item) {
return {};
}
- lastEntry = item.get();
-
switch (util::deviceToHost32(mapEntry.name.ident)) {
case ResTable_map::ATTR_ZERO:
plural->values[Plural::Zero] = std::move(item);
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
index 0745a59..12bc13d 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -55,14 +55,8 @@
bool parse();
private:
- // Helper method to retrieve the symbol name for a given table offset specified
- // as a pointer.
- Maybe<Reference> getSymbol(const void* data);
-
bool parseTable(const android::ResChunk_header* chunk);
- bool parseSymbolTable(const android::ResChunk_header* chunk);
bool parsePackage(const android::ResChunk_header* chunk);
- bool parsePublic(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
bool parseTypeSpec(const android::ResChunk_header* chunk);
bool parseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
@@ -87,10 +81,6 @@
const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Styleable> parseStyleable(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
-
/**
* If the mapEntry is a special type that denotes meta data (source, comment), then it is
* read and added to the Value.
@@ -106,23 +96,6 @@
const void* mData;
const size_t mDataLen;
- // The array of symbol entries. Each element points to an offset
- // in the table and an index into the symbol table string pool.
- const SymbolTable_entry* mSymbolEntries = nullptr;
-
- // Number of symbol entries.
- size_t mSymbolEntryCount = 0;
-
- // The symbol table string pool. Holds the names of symbols
- // referenced in this table but not defined nor resolved to an
- // ID.
- android::ResStringPool mSymbolPool;
-
- // The source string pool. Resource entries may have an extra
- // field that points into this string pool, which denotes where
- // the resource was parsed from originally.
- android::ResStringPool mSourcePool;
-
// The standard value string pool for resource values.
android::ResStringPool mValuePool;
diff --git a/tools/aapt2/unflatten/FileExportHeaderReader.h b/tools/aapt2/unflatten/FileExportHeaderReader.h
deleted file mode 100644
index e552ea1..0000000
--- a/tools/aapt2/unflatten/FileExportHeaderReader.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H
-#define AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H
-
-#include "ResChunkPullParser.h"
-#include "Resource.h"
-#include "ResourceUtils.h"
-
-#include "flatten/ResourceTypeExtensions.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-
-#include <androidfw/ResourceTypes.h>
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-static ssize_t parseFileExportHeaderImpl(const void* data, const size_t len,
- const FileExport_header** outFileExport,
- const ExportedSymbol** outExportedSymbolIndices,
- android::ResStringPool* outStringPool,
- std::string* outError) {
- ResChunkPullParser parser(data, len);
- if (!ResChunkPullParser::isGoodEvent(parser.next())) {
- if (outError) *outError = parser.getLastError();
- return -1;
- }
-
- if (util::deviceToHost16(parser.getChunk()->type) != RES_FILE_EXPORT_TYPE) {
- if (outError) *outError = "no FileExport_header found";
- return -1;
- }
-
- const FileExport_header* fileExport = convertTo<FileExport_header>(parser.getChunk());
- if (!fileExport) {
- if (outError) *outError = "corrupt FileExport_header";
- return -1;
- }
-
- if (memcmp(fileExport->magic, "AAPT", sizeof(fileExport->magic)) != 0) {
- if (outError) *outError = "invalid magic value";
- return -1;
- }
-
- const size_t exportedSymbolCount = util::deviceToHost32(fileExport->exportedSymbolCount);
-
- // Verify that we have enough space for all those symbols.
- size_t dataLen = getChunkDataLen(&fileExport->header);
- if (exportedSymbolCount > dataLen / sizeof(ExportedSymbol)) {
- if (outError) *outError = "too many symbols";
- return -1;
- }
-
- const size_t symbolIndicesSize = exportedSymbolCount * sizeof(ExportedSymbol);
-
- const void* strPoolData = getChunkData(&fileExport->header) + symbolIndicesSize;
- const size_t strPoolDataLen = dataLen - symbolIndicesSize;
- if (outStringPool->setTo(strPoolData, strPoolDataLen, false) != android::NO_ERROR) {
- if (outError) *outError = "corrupt string pool";
- return -1;
- }
-
- *outFileExport = fileExport;
- *outExportedSymbolIndices = (const ExportedSymbol*) getChunkData(
- &fileExport->header);
- return util::deviceToHost16(fileExport->header.headerSize) + symbolIndicesSize +
- outStringPool->bytes();
-}
-
-static ssize_t getWrappedDataOffset(const void* data, size_t len, std::string* outError) {
- const FileExport_header* header = nullptr;
- const ExportedSymbol* entries = nullptr;
- android::ResStringPool pool;
- return parseFileExportHeaderImpl(data, len, &header, &entries, &pool, outError);
-}
-
-/**
- * Reads the FileExport_header and populates outRes with the values in that header.
- */
-static ssize_t unwrapFileExportHeader(const void* data, size_t len, ResourceFile* outRes,
- std::string* outError) {
-
- const FileExport_header* fileExport = nullptr;
- const ExportedSymbol* entries = nullptr;
- android::ResStringPool symbolPool;
- const ssize_t offset = parseFileExportHeaderImpl(data, len, &fileExport, &entries, &symbolPool,
- outError);
- if (offset < 0) {
- return offset;
- }
-
- const size_t exportedSymbolCount = util::deviceToHost32(fileExport->exportedSymbolCount);
- outRes->exportedSymbols.clear();
- outRes->exportedSymbols.reserve(exportedSymbolCount);
-
- for (size_t i = 0; i < exportedSymbolCount; i++) {
- const StringPiece16 str = util::getString(symbolPool,
- util::deviceToHost32(entries[i].name.index));
- StringPiece16 packageStr, typeStr, entryStr;
- ResourceUtils::extractResourceName(str, &packageStr, &typeStr, &entryStr);
- const ResourceType* resType = parseResourceType(typeStr);
- if (!resType || entryStr.empty()) {
- if (outError) {
- std::stringstream errorStr;
- errorStr << "invalid exported symbol at index="
- << util::deviceToHost32(entries[i].name.index)
- << " (" << str << ")";
- *outError = errorStr.str();
- }
- return -1;
- }
-
- outRes->exportedSymbols.push_back(SourcedResourceName{
- ResourceName{ packageStr.toString(), *resType, entryStr.toString() },
- util::deviceToHost32(entries[i].line) });
- }
-
- const StringPiece16 str = util::getString(symbolPool,
- util::deviceToHost32(fileExport->name.index));
- StringPiece16 packageStr, typeStr, entryStr;
- ResourceUtils::extractResourceName(str, &packageStr, &typeStr, &entryStr);
- const ResourceType* resType = parseResourceType(typeStr);
- if (!resType || entryStr.empty()) {
- if (outError) {
- std::stringstream errorStr;
- errorStr << "invalid resource name at index="
- << util::deviceToHost32(fileExport->name.index)
- << " (" << str << ")";
- *outError = errorStr.str();
- }
- return -1;
- }
-
- outRes->name = ResourceName{ packageStr.toString(), *resType, entryStr.toString() };
- outRes->source.path = util::utf16ToUtf8(
- util::getString(symbolPool, util::deviceToHost32(fileExport->source.index)));
- outRes->config.copyFromDtoH(fileExport->config);
- return offset;
-}
-
-} // namespace aapt
-
-#endif /* AAPT_UNFLATTEN_FILEEXPORTHEADERREADER_H */
diff --git a/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp b/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp
deleted file mode 100644
index a76c83b..0000000
--- a/tools/aapt2/unflatten/FileExportHeaderReader_test.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Resource.h"
-
-#include "flatten/FileExportWriter.h"
-#include "unflatten/FileExportHeaderReader.h"
-#include "util/BigBuffer.h"
-#include "util/Util.h"
-
-#include "test/Common.h"
-
-#include <gtest/gtest.h>
-
-namespace aapt {
-
-TEST(FileExportHeaderReaderTest, ReadHeaderWithNoSymbolExports) {
- ResourceFile resFile = {
- test::parseNameOrDie(u"@android:layout/main.xml"),
- test::parseConfigOrDie("sw600dp-v4"),
- Source{ "res/layout/main.xml" },
- };
-
- BigBuffer buffer(1024);
- ChunkWriter writer = wrapBufferWithFileExportHeader(&buffer, &resFile);
- *writer.getBuffer()->nextBlock<uint32_t>() = 42u;
- writer.finish();
-
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-
- ResourceFile actualResFile;
-
- ssize_t offset = unwrapFileExportHeader(data.get(), buffer.size(), &actualResFile, nullptr);
- ASSERT_GT(offset, 0);
-
- EXPECT_EQ(offset, getWrappedDataOffset(data.get(), buffer.size(), nullptr));
-
- EXPECT_EQ(actualResFile.config, test::parseConfigOrDie("sw600dp-v4"));
- EXPECT_EQ(actualResFile.name, test::parseNameOrDie(u"@android:layout/main.xml"));
- EXPECT_EQ(actualResFile.source.path, "res/layout/main.xml");
-
- EXPECT_EQ(*(uint32_t*)(data.get() + offset), 42u);
-}
-
-} // namespace aapt
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2560c31..8d1b124 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -348,6 +348,11 @@
}
@Override
+ public void notifyAppStopped(IBinder token) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java
index 611ed15..9fc1706 100644
--- a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java
+++ b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java
@@ -36,15 +36,15 @@
mMoTree = moTree;
}
- public String getmBaseUri() {
+ public String getBaseUri() {
return mBaseUri;
}
- public String getmUrn() {
+ public String getUrn() {
return mUrn;
}
- public String getmMoTree() {
+ public String getMoTree() {
return mMoTree;
}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 503e4a2..9137d9d 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -136,6 +136,9 @@
public static final int REASON_INVALID_REQUEST = -4;
/** Do not have required permission */
public static final int REASON_PERMISSION_DENIED = -5;
+ /** Ranging failed because responder role is enabled in STA mode.*/
+ public static final int
+ REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6;
public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description";
@@ -191,6 +194,8 @@
public int preambleSupported;
//RTT bandwidth supported
public int bwSupported;
+ // Whether STA responder role is supported.
+ public boolean responderSupported;
@Override
public String toString() {
@@ -244,6 +249,9 @@
sb.append("is supported.");
+ sb.append(" STA responder role is ")
+ .append(responderSupported ? "supported" : "not supported.");
+
return sb.toString();
}
/** Implement the Parcelable interface {@hide} */
@@ -261,7 +269,7 @@
dest.writeInt(lcrSupported ? 1 : 0);
dest.writeInt(preambleSupported);
dest.writeInt(bwSupported);
-
+ dest.writeInt(responderSupported ? 1 : 0);
}
/** Implement the Parcelable interface {@hide} */
@@ -275,6 +283,7 @@
capabilities.lcrSupported = in.readInt() == 1 ? true : false;
capabilities.preambleSupported = in.readInt();
capabilities.bwSupported = in.readInt();
+ capabilities.responderSupported = (in.readInt() == 1);
return capabilities;
}
/** Implement the Parcelable interface {@hide} */
@@ -898,6 +907,160 @@
sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
}
+ /**
+ * Callbacks for responder operations.
+ * <p>
+ * A {@link ResponderCallback} is the handle to the calling client. {@link RttManager} will keep
+ * a reference to the callback for the entire period when responder is enabled. The same
+ * callback as used in enabling responder needs to be passed for disabling responder.
+ * The client can freely destroy or reuse the callback after {@link RttManager#disableResponder}
+ * is called.
+ */
+ public abstract static class ResponderCallback {
+ /** Callback when responder is enabled. */
+ public abstract void onResponderEnabled(ResponderConfig config);
+ /** Callback when enabling responder failed. */
+ public abstract void onResponderEnableFailure(int reason);
+ // TODO: consider adding onResponderAborted once it's supported.
+ }
+
+ /**
+ * Enable Wi-Fi RTT responder mode on the device. The enabling result will be delivered via
+ * {@code callback}.
+ * <p>
+ * Note calling this method with the same callback when the responder is already enabled won't
+ * change the responder state, a cached {@link ResponderConfig} from the last enabling will be
+ * returned through the callback.
+ *
+ * @param callback Callback for responder enabling/disabling result.
+ * @throws IllegalArgumentException If {@code callback} is null.
+ */
+ public void enableResponder(ResponderCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ validateChannel();
+ int key = putListenerIfAbsent(callback);
+ sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ }
+
+ /**
+ * Disable Wi-Fi RTT responder mode on the device. The {@code callback} needs to be the
+ * same one used in {@link #enableResponder(ResponderCallback)}.
+ * <p>
+ * Calling this method when responder isn't enabled won't have any effect. The callback can be
+ * reused for enabling responder after this method is called.
+ *
+ * @param callback The same callback used for enabling responder.
+ * @throws IllegalArgumentException If {@code callback} is null.
+ */
+ public void disableResponder(ResponderCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ validateChannel();
+ int key = removeListener(callback);
+ if (key == INVALID_KEY) {
+ Log.e(TAG, "responder not enabled yet");
+ return;
+ }
+ sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ }
+
+ /**
+ * Configuration used for RTT responder mode. The configuration information can be used by a
+ * peer device to range the responder.
+ *
+ * @see ScanResult
+ */
+ public static class ResponderConfig implements Parcelable {
+
+ // TODO: make all fields final once we can get mac address from responder HAL APIs.
+ /**
+ * Wi-Fi mac address used for responder mode.
+ */
+ public String macAddress = "";
+
+ /**
+ * The primary 20 MHz frequency (in MHz) of the channel where responder is enabled.
+ * @see ScanResult#frequency
+ */
+ public int frequency;
+
+ /**
+ * Center frequency of the channel where responder is enabled on. Only in use when channel
+ * width is at least 40MHz.
+ * @see ScanResult#centerFreq0
+ */
+ public int centerFreq0;
+
+ /**
+ * Center frequency of the second segment when channel width is 80 + 80 MHz.
+ * @see ScanResult#centerFreq1
+ */
+ public int centerFreq1;
+
+ /**
+ * Width of the channel where responder is enabled on.
+ * @see ScanResult#channelWidth
+ */
+ public int channelWidth;
+
+ /**
+ * Preamble supported by responder.
+ */
+ public int preamble;
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("macAddress = ").append(macAddress)
+ .append(" frequency = ").append(frequency)
+ .append(" centerFreq0 = ").append(centerFreq0)
+ .append(" centerFreq1 = ").append(centerFreq1)
+ .append(" channelWidth = ").append(channelWidth)
+ .append(" preamble = ").append(preamble);
+ return builder.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(macAddress);
+ dest.writeInt(frequency);
+ dest.writeInt(centerFreq0);
+ dest.writeInt(centerFreq1);
+ dest.writeInt(channelWidth);
+ dest.writeInt(preamble);
+ }
+
+ /** Implement {@link Parcelable} interface */
+ public static final Parcelable.Creator<ResponderConfig> CREATOR =
+ new Parcelable.Creator<ResponderConfig>() {
+ @Override
+ public ResponderConfig createFromParcel(Parcel in) {
+ ResponderConfig config = new ResponderConfig();
+ config.macAddress = in.readString();
+ config.frequency = in.readInt();
+ config.centerFreq0 = in.readInt();
+ config.centerFreq1 = in.readInt();
+ config.channelWidth = in.readInt();
+ config.preamble = in.readInt();
+ return config;
+ }
+
+ @Override
+ public ResponderConfig[] newArray(int size) {
+ return new ResponderConfig[size];
+ }
+ };
+
+ }
+
/* private methods */
public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER;
@@ -906,6 +1069,12 @@
public static final int CMD_OP_FAILED = BASE + 2;
public static final int CMD_OP_SUCCEEDED = BASE + 3;
public static final int CMD_OP_ABORTED = BASE + 4;
+ public static final int CMD_OP_ENABLE_RESPONDER = BASE + 5;
+ public static final int CMD_OP_DISABLE_RESPONDER = BASE + 6;
+ public static final int
+ CMD_OP_ENALBE_RESPONDER_SUCCEEDED = BASE + 7;
+ public static final int
+ CMD_OP_ENALBE_RESPONDER_FAILED = BASE + 8;
private Context mContext;
private IRttManager mService;
@@ -992,6 +1161,23 @@
return key;
}
+ // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener.
+ private static int putListenerIfAbsent(Object listener) {
+ if (listener == null) return INVALID_KEY;
+ synchronized (sListenerMapLock) {
+ int key = getListenerKey(listener);
+ if (key != INVALID_KEY) {
+ return key;
+ }
+ do {
+ key = sListenerKey++;
+ } while (key == INVALID_KEY);
+ sListenerMap.put(key, listener);
+ return key;
+ }
+
+ }
+
private static Object getListener(int key) {
if (key == INVALID_KEY) return null;
synchronized (sListenerMapLock) {
@@ -1047,9 +1233,9 @@
// to fail and throw an exception
sAsyncChannel = null;
}
- sConnected.countDown();
return;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ sConnected.countDown();
return;
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
Log.e(TAG, "Channel connection lost");
@@ -1082,6 +1268,14 @@
((RttListener) listener).onAborted();
removeListener(msg.arg2);
break;
+ case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
+ ResponderConfig config = (ResponderConfig) msg.obj;
+ ((ResponderCallback) (listener)).onResponderEnabled(config);
+ break;
+ case CMD_OP_ENALBE_RESPONDER_FAILED:
+ ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
+ removeListener(msg.arg2);
+ break;
default:
if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
return;
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 4a86c59..31da670 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -332,6 +332,7 @@
public static class InformationElement {
public static final int EID_SSID = 0;
public static final int EID_BSS_LOAD = 11;
+ public static final int EID_RSN = 48;
public static final int EID_HT_OPERATION = 61;
public static final int EID_INTERWORKING = 107;
public static final int EID_ROAMING_CONSORTIUM = 111;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index a0dbd85..362738e 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -101,6 +101,8 @@
/** @hide */
public static final String CA_CERT_KEY = "ca_cert";
/** @hide */
+ public static final String CA_PATH_KEY = "ca_path";
+ /** @hide */
public static final String ENGINE_KEY = "engine";
/** @hide */
public static final String ENGINE_ID_KEY = "engine_id";
@@ -625,6 +627,33 @@
mCaCerts = null;
}
+ /**
+ * Set the ca_path directive on wpa_supplicant.
+ *
+ * From wpa_supplicant documentation:
+ *
+ * Directory path for CA certificate files (PEM). This path may contain
+ * multiple CA certificates in OpenSSL format. Common use for this is to
+ * point to system trusted CA list which is often installed into directory
+ * like /etc/ssl/certs. If configured, these certificates are added to the
+ * list of trusted CAs. ca_cert may also be included in that case, but it is
+ * not required.
+ * @param domain The path for CA certificate files
+ * @hide
+ */
+ public void setCaPath(String path) {
+ setFieldValue(CA_PATH_KEY, path);
+ }
+
+ /**
+ * Get the domain_suffix_match value. See setDomSuffixMatch.
+ * @return The path for CA certificate files.
+ * @hide
+ */
+ public String getCaPath() {
+ return getFieldValue(CA_PATH_KEY, "");
+ }
+
/** Set Client certificate alias.
*
* <p> See the {@link android.security.KeyChain} for details on installing or choosing