diff options
61 files changed, 937 insertions, 391 deletions
diff --git a/api/current.txt b/api/current.txt index 899381bec02d..82b6ae978722 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8040,8 +8040,8 @@ package android.bluetooth.le {      method public boolean isScannable();      method public void writeToParcel(android.os.Parcel, int);      field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR; -    field public static final int INTERVAL_HIGH = 160; // 0xa0 -    field public static final int INTERVAL_LOW = 1600; // 0x640 +    field public static final int INTERVAL_HIGH = 1600; // 0x640 +    field public static final int INTERVAL_LOW = 160; // 0xa0      field public static final int INTERVAL_MAX = 16777215; // 0xffffff      field public static final int INTERVAL_MEDIUM = 400; // 0x190      field public static final int INTERVAL_MIN = 160; // 0xa0 diff --git a/api/system-current.txt b/api/system-current.txt index 8aa6c362480d..fcc647d9e92c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8519,8 +8519,8 @@ package android.bluetooth.le {      method public boolean isScannable();      method public void writeToParcel(android.os.Parcel, int);      field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR; -    field public static final int INTERVAL_HIGH = 160; // 0xa0 -    field public static final int INTERVAL_LOW = 1600; // 0x640 +    field public static final int INTERVAL_HIGH = 1600; // 0x640 +    field public static final int INTERVAL_LOW = 160; // 0xa0      field public static final int INTERVAL_MAX = 16777215; // 0xffffff      field public static final int INTERVAL_MEDIUM = 400; // 0x190      field public static final int INTERVAL_MIN = 160; // 0xa0 diff --git a/api/test-current.txt b/api/test-current.txt index 944a35078e51..1ff9efe4dc52 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8071,8 +8071,8 @@ package android.bluetooth.le {      method public boolean isScannable();      method public void writeToParcel(android.os.Parcel, int);      field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR; -    field public static final int INTERVAL_HIGH = 160; // 0xa0 -    field public static final int INTERVAL_LOW = 1600; // 0x640 +    field public static final int INTERVAL_HIGH = 1600; // 0x640 +    field public static final int INTERVAL_LOW = 160; // 0xa0      field public static final int INTERVAL_MAX = 16777215; // 0xffffff      field public static final int INTERVAL_MEDIUM = 400; // 0x190      field public static final int INTERVAL_MIN = 160; // 0xa0 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 37c287eebf18..26ac6adce95d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -7261,7 +7261,7 @@ public class Activity extends ContextThemeWrapper              }          } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {              Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null; -            getAutofillManager().onAuthenticationResult(resultData); +            getAutofillManager().onAuthenticationResult(requestCode, resultData);          } else {              Fragment frag = mFragments.findFragmentByWho(who);              if (frag != null) { @@ -7406,10 +7406,11 @@ public class Activity extends ContextThemeWrapper      /** @hide */      @Override -    final public void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent) { +    final public void autofillCallbackAuthenticate(int authenticationId, IntentSender intent, +            Intent fillInIntent) {          try {              startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX, -                    0, fillInIntent, 0, 0, null); +                    authenticationId, fillInIntent, 0, 0, null);          } catch (IntentSender.SendIntentException e) {              Log.e(TAG, "authenticate() failed for intent:" + intent, e);          } diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java index 71c4484a9474..e9747d8205bf 100644 --- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -34,7 +34,7 @@ public final class AdvertisingSetParameters implements Parcelable {      * Advertise on low frequency, around every 1000ms. This is the default and      * preferred advertising mode as it consumes the least power.      */ -    public static final int INTERVAL_LOW = 1600; +    public static final int INTERVAL_HIGH = 1600;      /**       * Advertise on medium frequency, around every 250ms. This is balanced @@ -47,7 +47,7 @@ public final class AdvertisingSetParameters implements Parcelable {       * has the highest power consumption and should not be used for continuous       * background advertising.       */ -    public static final int INTERVAL_HIGH = 160; +    public static final int INTERVAL_LOW = 160;      /**       * Minimum value for advertising interval. diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 69f3f6723244..af2eb34f8751 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -162,14 +162,18 @@ public final class Dataset implements Parcelable {           *           * <p>When a user triggers autofill, the system launches the provided intent           * whose extras will have the {@link -         * android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen content}. Once -         * you complete your authentication flow you should set the activity result to {@link -         * android.app.Activity#RESULT_OK} and provide the fully populated {@link Dataset -         * dataset} by setting it to the {@link -         * android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. For example, -         * if you provided credit card information without the CVV for the data set in the -         * {@link FillResponse response} then the returned data set should contain the -         * CVV entry.</p> +         * android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen content}, +         * and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE client +         * state}. Once you complete your authentication flow you should set the activity +         * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated +         * {@link Dataset dataset} or a fully-populated {@link FillResponse response} by +         * setting it to the {@link +         * android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you +         * provide a dataset in the result, it will replace the authenticated dataset and +         * will be immediately filled in. If you provide a response, it will replace the +         * current response and the UI will be refreshed. For example, if you provided +         * credit card information without the CVV for the data set in the {@link FillResponse +         * response} then the returned data set should contain the CVV entry.           *           * <p></><strong>Note:</strong> Do not make the provided pending intent           * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the @@ -210,12 +214,15 @@ public final class Dataset implements Parcelable {           *           * @param id id returned by {@link           *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}. -         * @param value value to be auto filled. +         * @param value value to be auto filled. Pass {@code null} if you do not have the value +         *        but the target view is a logical part of the dataset. For example, if +         *        the dataset needs an authentication and you have no access to the value. +         *        Filtering matches any user typed string to {@code null} values.           * @return This builder.           * @throws IllegalStateException if the builder was constructed without a presentation           * ({@link RemoteViews}).           */ -        public @NonNull Builder setValue(@NonNull AutofillId id, @NonNull AutofillValue value) { +        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {              throwIfDestroyed();              if (mPresentation == null) {                  throw new IllegalStateException("Dataset presentation not set on constructor"); @@ -229,11 +236,14 @@ public final class Dataset implements Parcelable {           *           * @param id id returned by {@link           *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}. -         * @param value value to be auto filled. +         * @param value value to be auto filled. Pass {@code null} if you do not have the value +         *        but the target view is a logical part of the dataset. For example, if +         *        the dataset needs an authentication and you have no access to the value. +         *        Filtering matches any user typed string to {@code null} values.           * @param presentation The presentation used to visualize this field.           * @return This builder.           */ -        public @NonNull Builder setValue(@NonNull AutofillId id, @NonNull AutofillValue value, +        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,                  @NonNull RemoteViews presentation) {              throwIfDestroyed();              Preconditions.checkNotNull(presentation, "presentation cannot be null"); @@ -244,7 +254,6 @@ public final class Dataset implements Parcelable {          private void setValueAndPresentation(AutofillId id, AutofillValue value,                  RemoteViews presentation) {              Preconditions.checkNotNull(id, "id cannot be null"); -            Preconditions.checkNotNull(value, "value cannot be null");              if (mFieldIds != null) {                  final int existingIdx = mFieldIds.indexOf(id);                  if (existingIdx >= 0) { diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 1914db9eb438..bc96e439811f 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -236,7 +236,8 @@ public final class FillResponse implements Parcelable {           *           * <p>When a user triggers autofill, the system launches the provided intent           * whose extras will have the {@link AutofillManager#EXTRA_ASSIST_STRUCTURE screen -         * content}. Once you complete your authentication flow you should set the activity +         * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE +         * client state}. Once you complete your authentication flow you should set the activity           * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated           * {@link FillResponse response} by setting it to the {@link           * AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index c7151db3cb5c..75a9965e32a8 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -117,6 +117,48 @@ public final class AutofillManager {      /** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;      /** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4; +    /** Which bits in an authentication id are used for the dataset id */ +    private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF; +    /** How many bits in an authentication id are used for the dataset id */ +    private static final int AUTHENTICATION_ID_DATASET_ID_SHIFT = 16; +    /** @hide The index for an undefined data set */ +    public static final int AUTHENTICATION_ID_DATASET_ID_UNDEFINED = 0xFFFF; + +    /** +     * Makes an authentication id from a request id and a dataset id. +     * +     * @param requestId The request id. +     * @param datasetId The dataset id. +     * @return The authentication id. +     * @hide +     */ +    public static int makeAuthenticationId(int requestId, int datasetId) { +        return (requestId << AUTHENTICATION_ID_DATASET_ID_SHIFT) +                | (datasetId & AUTHENTICATION_ID_DATASET_ID_MASK); +    } + +    /** +     * Gets the request id from an authentication id. +     * +     * @param authRequestId The authentication id. +     * @return The request id. +     * @hide +     */ +    public static int getRequestIdFromAuthenticationId(int authRequestId) { +        return (authRequestId >> AUTHENTICATION_ID_DATASET_ID_SHIFT); +    } + +    /** +     * Gets the dataset id from an authentication id. +     * +     * @param authRequestId The authentication id. +     * @return The dataset id. +     * @hide +     */ +    public static int getDatasetIdFromAuthenticationId(int authRequestId) { +        return (authRequestId & AUTHENTICATION_ID_DATASET_ID_MASK); +    } +      private final MetricsLogger mMetricsLogger = new MetricsLogger();      /** @@ -156,10 +198,12 @@ public final class AutofillManager {          /**           * Asks the client to start an authentication flow.           * +         * @param authenticationId A unique id of the authentication operation.           * @param intent The authentication intent.           * @param fillInIntent The authentication fill-in intent.           */ -        void autofillCallbackAuthenticate(IntentSender intent, Intent fillInIntent); +        void autofillCallbackAuthenticate(int authenticationId, IntentSender intent, +                Intent fillInIntent);          /**           * Tells the client this manager has state to be reset. @@ -675,7 +719,7 @@ public final class AutofillManager {      }      /** @hide */ -    public void onAuthenticationResult(Intent data) { +    public void onAuthenticationResult(int authenticationId, Intent data) {          if (!hasAutofillFeature()) {              return;          } @@ -694,7 +738,8 @@ public final class AutofillManager {              final Bundle responseData = new Bundle();              responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result);              try { -                mService.setAuthenticationResult(responseData, mSessionId, mContext.getUserId()); +                mService.setAuthenticationResult(responseData, mSessionId, authenticationId, +                        mContext.getUserId());              } catch (RemoteException e) {                  Log.e(TAG, "Error delivering authentication result", e);              } @@ -870,12 +915,13 @@ public final class AutofillManager {          }      } -    private void authenticate(int sessionId, IntentSender intent, Intent fillInIntent) { +    private void authenticate(int sessionId, int authenticationId, IntentSender intent, +            Intent fillInIntent) {          synchronized (mLock) {              if (sessionId == mSessionId) {                  AutofillClient client = getClientLocked();                  if (client != null) { -                    client.autofillCallbackAuthenticate(intent, fillInIntent); +                    client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);                  }              }          } @@ -1393,11 +1439,12 @@ public final class AutofillManager {          }          @Override -        public void authenticate(int sessionId, IntentSender intent, Intent fillInIntent) { +        public void authenticate(int sessionId, int authenticationId, IntentSender intent, +                Intent fillInIntent) {              final AutofillManager afm = mAfm.get();              if (afm != null) {                  afm.mContext.getMainThreadHandler().post( -                        () -> afm.authenticate(sessionId, intent, fillInIntent)); +                        () -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));              }          } diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 4193a3cbf51c..a12e9560115a 100644 --- a/core/java/android/view/autofill/IAutoFillManager.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -41,7 +41,7 @@ interface IAutoFillManager {              in AutofillValue value, int action, int flags, int userId);      void finishSession(int sessionId, int userId);      void cancelSession(int sessionId, int userId); -    void setAuthenticationResult(in Bundle data, int sessionId, int userId); +    void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);      void setHasCallback(int sessionId, int userId, boolean hasIt);      void disableOwnedAutofillServices(int userId);      boolean isServiceSupported(int userId); diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index 825d311a5b3b..1d66f7f7f463 100644 --- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -45,7 +45,8 @@ oneway interface IAutoFillManagerClient {      /**        * Authenticates a fill response or a data set.        */ -    void authenticate(int sessionId, in IntentSender intent, in Intent fillInIntent); +    void authenticate(int sessionId, int authenticationId, in IntentSender intent, +            in Intent fillInIntent);      /**        * Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 569fe017ac86..5845719ee537 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable;  import android.os.Bundle;  import android.os.Trace;  import android.util.AttributeSet; +import android.util.Log;  import android.util.MathUtils;  import android.util.SparseBooleanArray;  import android.view.FocusFinder; @@ -147,6 +148,8 @@ import java.util.function.Predicate;   */  @RemoteView  public class ListView extends AbsListView { +    static final String TAG = "ListView"; +      /**       * Used to indicate a no preference for a position type.       */ @@ -335,6 +338,12 @@ public class ListView extends AbsListView {       * @param isSelectable whether the item is selectable       */      public void addHeaderView(View v, Object data, boolean isSelectable) { +        if (v.getParent() != null && v.getParent() != this) { +            if (Log.isLoggable(TAG, Log.WARN)) { +                Log.w(TAG, "The specified child already has a parent. " +                           + "You must call removeView() on the child's parent first."); +            } +        }          final FixedViewInfo info = new FixedViewInfo();          info.view = v;          info.data = data; @@ -429,6 +438,13 @@ public class ListView extends AbsListView {       * @param isSelectable true if the footer view can be selected       */      public void addFooterView(View v, Object data, boolean isSelectable) { +        if (v.getParent() != null && v.getParent() != this) { +            if (Log.isLoggable(TAG, Log.WARN)) { +                Log.w(TAG, "The specified child already has a parent. " +                           + "You must call removeView() on the child's parent first."); +            } +        } +          final FixedViewInfo info = new FixedViewInfo();          info.view = v;          info.data = data; diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java index c6cb8376d80f..ddf07f400ada 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -80,8 +80,8 @@ public class AmbientDisplayConfiguration {      }      public boolean alwaysOnAvailable() { -        // TODO: introduce config_dozeAlwaysOnAvailable. For now just debuggable builds. -        return Build.IS_DEBUGGABLE && ambientDisplayAvailable(); +        // Does not work properly yet. +        return false;      }      public String ambientDisplayComponent() { diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 27f98b3c2c45..72011e87c61e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4629,6 +4629,15 @@      <!-- Toast message shown when user manually request autofill but service could not figure out the data that would autofill the screen contents. [CHAR LIMIT=NONE] -->      <string name="autofill_error_cannot_autofill">Contents can\u2019t be autofilled</string> +    <!-- Accessibility string to announce there are no autofill suggestions in the autofill picker. [CHAR LIMIT=NONE] --> +    <string name="autofill_picker_no_suggestions">No autofill suggestions</string> + +    <!-- Accessibility string to announce there are some autofill suggestions in the autofill picker. [CHAR LIMIT=NONE] --> +    <plurals name="autofill_picker_some_suggestions"> +        <item quantity="one">One autofill suggestion</item> +        <item quantity="other"><xliff:g id="count" example="Two">%1$s</xliff:g> autofill suggestions</item> +    </plurals> +      <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved           by an autofill service, but the service does not know what the activity represents [CHAR LIMIT=NONE] -->      <string name="autofill_save_title">Save to <b><xliff:g id="label" example="MyPass">%1$s</xliff:g></b>?</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cff6eb198473..2aab59035b2c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2891,6 +2891,8 @@    <java-symbol type="id" name="autofill_save_yes" />    <java-symbol type="id" name="autofill_save_close" />    <java-symbol type="string" name="autofill_error_cannot_autofill" /> +  <java-symbol type="string" name="autofill_picker_no_suggestions" /> +  <java-symbol type="plurals" name="autofill_picker_some_suggestions" />    <java-symbol type="string" name="autofill" />    <java-symbol type="string" name="autofill_picker_accessibility_title " />    <java-symbol type="string" name="autofill_save_accessibility_title " /> diff --git a/core/tests/coretests/src/android/view/DisabledTest.java b/core/tests/coretests/src/android/view/DisabledTest.java index 992c277e51d3..d78e5e9bcbbd 100644 --- a/core/tests/coretests/src/android/view/DisabledTest.java +++ b/core/tests/coretests/src/android/view/DisabledTest.java @@ -16,15 +16,15 @@  package android.view; -import com.android.frameworks.coretests.R; +import android.test.ActivityInstrumentationTestCase;  import android.test.TouchUtils; -import android.test.suitebuilder.annotation.MediumTest;  import android.test.suitebuilder.annotation.LargeTest; - -import android.test.ActivityInstrumentationTestCase; -import android.widget.Button; +import android.test.suitebuilder.annotation.MediumTest;  import android.view.KeyEvent;  import android.view.View; +import android.widget.Button; + +import com.android.frameworks.coretests.R;  /**   * Exercises {@link android.view.View}'s disabled property. @@ -45,18 +45,23 @@ public class DisabledTest extends ActivityInstrumentationTestCase<Disabled> {          final Disabled a = getActivity();          mDisabled = (Button) a.findViewById(R.id.disabledButton); -        mDisabled.setOnClickListener(new View.OnClickListener() { -            public void onClick(View v) { -                mClicked = true; -            } -        }); -          mDisabledParent = a.findViewById(R.id.clickableParent); -        mDisabledParent.setOnClickListener(new View.OnClickListener() { -            public void onClick(View v) { -                mParentClicked = true; -            } -        }); +        getInstrumentation().runOnMainSync( +                new Runnable() { +                    @Override +                    public void run() { +                        mDisabled.setOnClickListener(new View.OnClickListener() { +                            public void onClick(View v) { +                                mClicked = true; +                            } +                        }); +                        mDisabledParent.setOnClickListener(new View.OnClickListener() { +                            public void onClick(View v) { +                                mParentClicked = true; +                            } +                        }); +                    } +                });      }      @Override diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index b958c2873019..f844cc163bbe 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -17,6 +17,7 @@  package com.android.externalstorage;  import android.annotation.Nullable; +import android.app.usage.StorageStatsManager;  import android.content.ContentResolver;  import android.content.Context;  import android.content.UriPermission; @@ -49,10 +50,12 @@ import com.android.internal.util.IndentingPrintWriter;  import java.io.File;  import java.io.FileDescriptor;  import java.io.FileNotFoundException; +import java.io.IOException;  import java.io.PrintWriter;  import java.util.Collections;  import java.util.List;  import java.util.Objects; +import java.util.UUID;  public class ExternalStorageProvider extends FileSystemProvider {      private static final String TAG = "ExternalStorage"; @@ -79,6 +82,7 @@ public class ExternalStorageProvider extends FileSystemProvider {      private static class RootInfo {          public String rootId;          public String volumeId; +        public UUID storageUuid;          public int flags;          public String title;          public String docId; @@ -124,6 +128,7 @@ public class ExternalStorageProvider extends FileSystemProvider {              final String rootId;              final String title; +            final UUID storageUuid;              if (volume.getType() == VolumeInfo.TYPE_EMULATED) {                  // We currently only support a single emulated volume mounted at                  // a time, and it's always considered the primary @@ -142,17 +147,20 @@ public class ExternalStorageProvider extends FileSystemProvider {                      title = !TextUtils.isEmpty(deviceName)                              ? deviceName                              : getContext().getString(R.string.root_internal_storage); +                    storageUuid = StorageManager.UUID_DEFAULT;                  } else {                      // This should cover all other storage devices, like an SD card                      // or USB OTG drive plugged in. Using getBestVolumeDescription()                      // will give us a nice string like "Samsung SD card" or "SanDisk USB drive"                      final VolumeInfo privateVol = mStorageManager.findPrivateForEmulated(volume);                      title = mStorageManager.getBestVolumeDescription(privateVol); +                    storageUuid = StorageManager.convert(privateVol.fsUuid);                  }              } else if (volume.getType() == VolumeInfo.TYPE_PUBLIC                      && volume.getMountUserId() == userId) {                  rootId = volume.getFsUuid();                  title = mStorageManager.getBestVolumeDescription(volume); +                storageUuid = null;              } else {                  // Unsupported volume; ignore                  continue; @@ -172,6 +180,7 @@ public class ExternalStorageProvider extends FileSystemProvider {              root.rootId = rootId;              root.volumeId = volume.id; +            root.storageUuid = storageUuid;              root.flags = Root.FLAG_LOCAL_ONLY                      | Root.FLAG_SUPPORTS_SEARCH                      | Root.FLAG_SUPPORTS_IS_CHILD; @@ -385,8 +394,22 @@ public class ExternalStorageProvider extends FileSystemProvider {                  row.add(Root.COLUMN_FLAGS, root.flags);                  row.add(Root.COLUMN_TITLE, root.title);                  row.add(Root.COLUMN_DOCUMENT_ID, root.docId); -                row.add(Root.COLUMN_AVAILABLE_BYTES, -                        root.reportAvailableBytes ? root.path.getUsableSpace() : -1); + +                long availableBytes = -1; +                if (root.reportAvailableBytes) { +                    if (root.storageUuid != null) { +                        try { +                            availableBytes = getContext() +                                    .getSystemService(StorageStatsManager.class) +                                    .getFreeBytes(root.storageUuid); +                        } catch (IOException e) { +                            Log.w(TAG, e); +                        } +                    } else { +                        availableBytes = root.path.getUsableSpace(); +                    } +                } +                row.add(Root.COLUMN_AVAILABLE_BYTES, availableBytes);              }          }          return result; diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index 4950af3e14e0..9b5982b96e31 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -200,10 +200,7 @@ class MtpDatabase {                      storageCursor.close();                  } -                final RowBuilder row = result.newRow(); -                for (final String key : values.keySet()) { -                    row.add(key, values.get(key)); -                } +                putValuesToCursor(values, result);              }              return result; @@ -760,7 +757,9 @@ class MtpDatabase {                  Document.MIME_TYPE_DIR,                  0,                  MtpConstants.PROTECTION_STATUS_NONE, -                DOCUMENT_TYPE_DEVICE)); +                // Storages are placed under device so we cannot create a document just under +                // device. +                DOCUMENT_TYPE_DEVICE) & ~Document.FLAG_DIR_SUPPORTS_CREATE);          values.putNull(Document.COLUMN_SIZE);          extraValues.clear(); @@ -915,6 +914,13 @@ class MtpDatabase {          return results;      } +    static void putValuesToCursor(ContentValues values, MatrixCursor cursor) { +        final RowBuilder row = cursor.newRow(); +        for (final String name : cursor.getColumnNames()) { +            row.add(values.get(name)); +        } +    } +      private static String getIdList(Set<String> ids) {          String result = "(";          for (final String id : ids) { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index db88f2c038ec..eb2d8aa71955 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -17,11 +17,13 @@  package com.android.mtp;  import android.content.ContentResolver; +import android.content.ContentValues;  import android.content.Context;  import android.content.UriPermission;  import android.content.res.AssetFileDescriptor;  import android.content.res.Resources;  import android.database.Cursor; +import android.database.DatabaseUtils;  import android.database.MatrixCursor;  import android.database.sqlite.SQLiteDiskIOException;  import android.graphics.Point; @@ -55,7 +57,6 @@ import java.util.LinkedList;  import java.util.List;  import java.util.Map;  import java.util.concurrent.TimeoutException; -  import libcore.io.IoUtils;  /** @@ -177,7 +178,57 @@ public class MtpDocumentsProvider extends DocumentsProvider {          if (projection == null) {              projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;          } -        return mDatabase.queryDocument(documentId, projection); +        final Cursor cursor = mDatabase.queryDocument(documentId, projection); +        final int cursorCount = cursor.getCount(); +        if (cursorCount == 0) { +            cursor.close(); +            throw new FileNotFoundException(); +        } else if (cursorCount != 1) { +            cursor.close(); +            Log.wtf(TAG, "Unexpected cursor size: " + cursorCount); +            return null; +        } + +        final Identifier identifier = mDatabase.createIdentifier(documentId); +        if (identifier.mDocumentType != MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) { +            return cursor; +        } +        final String[] storageDocIds = mDatabase.getStorageDocumentIds(documentId); +        if (storageDocIds.length != 1) { +            return mDatabase.queryDocument(documentId, projection); +        } + +        // If the documentId specifies a device having exact one storage, we repalce some device +        // attributes with the storage attributes. +        try { +            final String storageName; +            final int storageFlags; +            try (final Cursor storageCursor = mDatabase.queryDocument( +                    storageDocIds[0], +                    MtpDatabase.strings(Document.COLUMN_DISPLAY_NAME, Document.COLUMN_FLAGS))) { +                if (!storageCursor.moveToNext()) { +                    throw new FileNotFoundException(); +                } +                storageName = storageCursor.getString(0); +                storageFlags = storageCursor.getInt(1); +            } + +            cursor.moveToNext(); +            final ContentValues values = new ContentValues(); +            DatabaseUtils.cursorRowToContentValues(cursor, values); +            if (values.containsKey(Document.COLUMN_DISPLAY_NAME)) { +                values.put(Document.COLUMN_DISPLAY_NAME, mResources.getString( +                        R.string.root_name, +                        values.getAsString(Document.COLUMN_DISPLAY_NAME), +                        storageName)); +            } +            values.put(Document.COLUMN_FLAGS, storageFlags); +            final MatrixCursor output = new MatrixCursor(projection, 1); +            MtpDatabase.putValuesToCursor(values, output); +            return output; +        } finally { +            cursor.close(); +        }      }      @Override diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index c9420d13a6e5..3fa5eb581ef7 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -368,7 +368,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {          assertEquals(0, cursor.getInt(5));      } -    public void testQueryDocument_forRoot() +    public void testQueryDocument_forStorage()              throws IOException, InterruptedException, TimeoutException {          setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);          setupRoots(0, new MtpRoot[] { @@ -392,6 +392,61 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {          assertEquals(3072, cursor.getInt(5));      } +    public void testQueryDocument_forDeviceWithSingleStorage() +            throws IOException, InterruptedException, TimeoutException { +        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); +        setupRoots(0, new MtpRoot[] { +                new MtpRoot( +                        0 /* deviceId */, +                        1 /* storageId */, +                        "Storage A" /* volume description */, +                        1024 /* free space */, +                        4096 /* total space */, +                        "" /* no volume identifier */) +        }); +        final Cursor cursor = mProvider.queryDocument("1", null); +        assertEquals(1, cursor.getCount()); + +        cursor.moveToNext(); +        assertEquals("1", cursor.getString(0)); +        assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1)); +        assertEquals("Device Storage A", cursor.getString(2)); +        assertTrue(cursor.isNull(3)); +        assertEquals(DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE, cursor.getInt(4)); +        assertTrue(cursor.isNull(5)); +    } + +    public void testQueryDocument_forDeviceWithTwoStorages() +            throws IOException, InterruptedException, TimeoutException { +        setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); +        setupRoots(0, new MtpRoot[] { +                new MtpRoot( +                        0 /* deviceId */, +                        1 /* storageId */, +                        "Storage A" /* volume description */, +                        1024 /* free space */, +                        4096 /* total space */, +                        "" /* no volume identifier */), +                new MtpRoot( +                        0 /* deviceId */, +                        2 /* storageId */, +                        "Storage B" /* volume description */, +                        1024 /* free space */, +                        4096 /* total space */, +                        "" /* no volume identifier */) +        }); +        final Cursor cursor = mProvider.queryDocument("1", null); +        assertEquals(1, cursor.getCount()); + +        cursor.moveToNext(); +        assertEquals("1", cursor.getString(0)); +        assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1)); +        assertEquals("Device", cursor.getString(2)); +        assertTrue(cursor.isNull(3)); +        assertEquals(0, cursor.getInt(4)); +        assertTrue(cursor.isNull(5)); +    } +      public void testQueryChildDocuments() throws Exception {          setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);          setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") }); diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 21b4fbce0da2..841c96eeda9b 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -82,12 +82,12 @@      <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ভুল পিন বা পাস কী দেওয়ার কারণে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সঙ্গে যুক্ত করা যায়নি।"</string>      <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> এর সঙ্গে যোগাযোগ করতে পারবেন না।"</string>      <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"যুক্ত করা <xliff:g id="DEVICE_NAME">%1$s</xliff:g> প্রত্যাখ্যান করেছে।"</string> -    <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wifi বন্ধ৷"</string> -    <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wifi এর সংযোগ বিচ্ছিন্ন হয়েছে৷"</string> -    <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wifi এ একটি দণ্ড৷"</string> -    <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wifi এ দুইটি দণ্ড৷"</string> -    <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wifi এ তিনটি দণ্ড৷"</string> -    <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wifi এ সম্পূর্ণ সিগন্যাল৷"</string> +    <string name="accessibility_wifi_off" msgid="1166761729660614716">"ওয়াই ফাই বন্ধ৷"</string> +    <string name="accessibility_no_wifi" msgid="8834610636137374508">"ওয়াই ফাই এর সংযোগ বিচ্ছিন্ন হয়েছে৷"</string> +    <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"ওয়াই ফাই এ একটি দণ্ড৷"</string> +    <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"ওয়াই ফাই এ দুইটি দণ্ড৷"</string> +    <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"ওয়াই ফাই এ তিনটি দণ্ড৷"</string> +    <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"ওয়াই ফাই এ সম্পূর্ণ সিগন্যাল৷"</string>      <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>      <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"সরানো অ্যাপ্লিকেশানগুলি"</string>      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"সরানো অ্যাপ্লিকেশানগুলি এবং ব্যবহারকারীগণ"</string> @@ -171,7 +171,7 @@      <string name="wifi_display_certification" msgid="8611569543791307533">"ওয়্যারলেস ডিসপ্লে সার্টিফিকেশন"</string>      <string name="wifi_verbose_logging" msgid="4203729756047242344">"ওয়াই-ফাই ভারবোস লগিং সক্ষম করুন"</string>      <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ওয়াই-ফাই থেকে মোবাইলে তৎপর হস্তান্তর"</string> -    <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"সর্বদা Wifi রোম স্ক্যানকে অনুমতি দিন"</string> +    <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"সর্বদা ওয়াই ফাই রোম স্ক্যানকে অনুমতি দিন"</string>      <string name="mobile_data_always_on" msgid="8774857027458200434">"মোবাইল ডেটা সব সময় সক্রিয় থাক"</string>      <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"চূড়ান্ত ভলিউম অক্ষম করুন"</string>      <string name="bluetooth_enable_inband_ringing" msgid="3291686366721786740">"ইন-ব্যান্ড রিং করা সক্ষম করুন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 6d755ea8f979..451a1ff51186 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -114,8 +114,8 @@      <string name="tts_default_lang_summary" msgid="5219362163902707785">"Postavlja glas za dati jezik za izgovoreni tekst"</string>      <string name="tts_play_example_title" msgid="7094780383253097230">"Poslušajte primjer"</string>      <string name="tts_play_example_summary" msgid="8029071615047894486">"Reproduciraj kratku demonstraciju sintetiziranja govora"</string> -    <string name="tts_install_data_title" msgid="4264378440508149986">"Instaliraj glasovne podatke"</string> -    <string name="tts_install_data_summary" msgid="5742135732511822589">"Instalirajte glasovne podatke potrebne za sintetiziranje govora"</string> +    <string name="tts_install_data_title" msgid="4264378440508149986">"Instaliranje glasovnih podataka"</string> +    <string name="tts_install_data_summary" msgid="5742135732511822589">"Instaliranje glasovnih podataka potrebnih za sintetiziranje govora"</string>      <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ovaj program za sintetiziranje govora u mogućnosti je da prikuplja sav tekst koji se izgovara, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Program omogućava aplikacija <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Da li želite koristiti ovaj program za sintetiziranje govora?"</string>      <string name="tts_engine_network_required" msgid="1190837151485314743">"Ovaj jezik zahtijeva aktivnu mrežnu vezu za pretvaranje teksta u govor."</string>      <string name="tts_default_sample_string" msgid="4040835213373086322">"Ovo je primjer sinteze govora"</string> @@ -319,7 +319,7 @@      <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može uticati na performanse."</string>      <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>      <string name="power_remaining_duration_only" msgid="845431008899029842">"Preostalo je otprilike još <xliff:g id="TIME">%1$s</xliff:g>"</string> -    <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Do potpune napunjenosti preostalo je <xliff:g id="TIME">%1$s</xliff:g>"</string> +    <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>      <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Imate još <xliff:g id="TIME">%1$s</xliff:g>"</string>      <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - imate još <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> - imate još <xliff:g id="TIME">%2$s</xliff:g>"</string> @@ -327,7 +327,7 @@      <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>      <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string> -    <string name="battery_info_status_charging" msgid="1705179948350365604">"Puni se"</string> +    <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>      <string name="battery_info_status_charging_lower" msgid="8689770213898117994">"punjenje"</string>      <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>      <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 13dde32f6493..39d4a7609bda 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -31,9 +31,9 @@      <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de rango"</string>      <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No se establecerá conexión automáticamente"</string>      <string name="wifi_no_internet" msgid="3880396223819116454">"No se ha detectado acceso a Internet"</string> -    <string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string> +    <string name="saved_network" msgid="4352716707126620811">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>      <string name="connected_via_network_scorer" msgid="5713793306870815341">"Conectada automáticamente a través de %1$s"</string> -    <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Conectada automáticamente a través de un proveedor de valoración de red"</string> +    <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Conectado automáticamente a través de un proveedor de valoración de redes"</string>      <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>      <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>      <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conexión sin Internet"</string> @@ -315,7 +315,7 @@      <string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"Deuteronomalía (rojo-verde)"</string>      <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"Protanomalía (rojo-verde)"</string>      <string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"Tritanomalía (azul-amarillo)"</string> -    <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección del color"</string> +    <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección de color"</string>      <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar al rendimiento."</string>      <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>      <string name="power_remaining_duration_only" msgid="845431008899029842">"Tiempo restante aproximado: <xliff:g id="TIME">%1$s</xliff:g>"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index cc528622910a..36d19effba1c 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -93,7 +93,7 @@      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Eemaldatud rakendused ja kasutajad"</string>      <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB jagamine"</string>      <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Kantav tööpunkt"</string> -    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetoothi jagamine"</string> +    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Jagamine Bluetoothiga"</string>      <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Jagamine"</string>      <string name="tether_settings_title_all" msgid="8356136101061143841">"Jagam. ja kant. kuumkoht"</string>      <string name="managed_user_title" msgid="8109605045406748842">"Kõik töörakendused"</string> @@ -114,8 +114,8 @@      <string name="tts_default_lang_summary" msgid="5219362163902707785">"Määrab räägitud teksti keelespetsiifilise hääle"</string>      <string name="tts_play_example_title" msgid="7094780383253097230">"Kuulake näidet"</string>      <string name="tts_play_example_summary" msgid="8029071615047894486">"Esita lühike kõnesünteesi demo"</string> -    <string name="tts_install_data_title" msgid="4264378440508149986">"Installi hääleandmed"</string> -    <string name="tts_install_data_summary" msgid="5742135732511822589">"Installi kõnesünteesi jaoks vajalikud hääleandmed"</string> +    <string name="tts_install_data_title" msgid="4264378440508149986">"Häälandmete installimine"</string> +    <string name="tts_install_data_summary" msgid="5742135732511822589">"Installige kõnesünteesi jaoks vajalikud häälandmed"</string>      <string name="tts_engine_security_warning" msgid="8786238102020223650">"See kõnesünteesimootor võib koguda kogu kõneldud teksti, sh isiklikke andmeid, nagu paroolid ja krediitkaardinumbrid. Selle aluseks on mootor <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Kas lubada kõnesünteesimootori kasutamine?"</string>      <string name="tts_engine_network_required" msgid="1190837151485314743">"Selle keele puhul on kõnesünteesi väljundi jaoks vaja toimivat võrguühendust."</string>      <string name="tts_default_sample_string" msgid="4040835213373086322">"See on kõnesünteesi näide"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 155f1c5a82a8..5d29ec0a238e 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -31,7 +31,7 @@      <string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>      <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Reconnexion automatique impossible"</string>      <string name="wifi_no_internet" msgid="3880396223819116454">"Aucun accès à Internet"</string> -    <string name="saved_network" msgid="4352716707126620811">"Enregistré par <xliff:g id="NAME">%1$s</xliff:g>"</string> +    <string name="saved_network" msgid="4352716707126620811">"Enregistré par : <xliff:g id="NAME">%1$s</xliff:g>"</string>      <string name="connected_via_network_scorer" msgid="5713793306870815341">"Connecté automatiquement via %1$s"</string>      <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string>      <string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté via %1$s"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 8b57734635d3..929332fd2b6f 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -324,7 +324,7 @@      <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> (tempo restante aproximado: <xliff:g id="TIME">%2$s</xliff:g>)"</string>      <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> (tempo restante: <xliff:g id="TIME">%2$s</xliff:g>)"</string>      <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> -    <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ata que cargue por completo"</string> +    <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ata completar a carga"</string>      <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g>)"</string>      <string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string>      <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 14ba22334ea9..8dd85c3c80e4 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -31,7 +31,7 @@      <string name="wifi_not_in_range" msgid="1136191511238508967">"Ընդգրկույթից դուրս է"</string>      <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Չի միանա ավտոմատ"</string>      <string name="wifi_no_internet" msgid="3880396223819116454">"Ինտերնետ կապ չկա"</string> -    <string name="saved_network" msgid="4352716707126620811">"Պահել է հետևյալ օգտատերը՝ <xliff:g id="NAME">%1$s</xliff:g>"</string> +    <string name="saved_network" msgid="4352716707126620811">"Պահել է՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>      <string name="connected_via_network_scorer" msgid="5713793306870815341">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>      <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Ավտոմատ կերպով միացել է ցանցի վարկանիշի ծառայության մատակարարի միջոցով"</string>      <string name="connected_via_passpoint" msgid="2826205693803088747">"Կապակցված է %1$s-ի միջոցով"</string> @@ -216,7 +216,7 @@      <string name="enable_terminal_title" msgid="95572094356054120">"Տեղային տերմինալ"</string>      <string name="enable_terminal_summary" msgid="67667852659359206">"Միացնել տերմինալային հավելվածը, որն առաջարկում է մուտք տեղային խեցի"</string>      <string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP ստուգում"</string> -    <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"Կարգավորել HDCP ստուգման վարքագիծը"</string> +    <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"HDCP-ի ստուգման կարգը"</string>      <string name="debug_debugging_category" msgid="6781250159513471316">"Վրիպազերծում"</string>      <string name="debug_app" msgid="8349591734751384446">"Ընտրել վրիպազերծման հավելվածը"</string>      <string name="debug_app_not_set" msgid="718752499586403499">"Վրիպազերծման ծրագիրը կարգավորված չէ"</string> @@ -294,7 +294,7 @@      <item msgid="8280754435979370728">"Բնական գույներ"</item>      <item msgid="5363960654009010371">"Թվային բովանդակության համար հարմարեցված գույներ"</item>    </string-array> -    <string name="inactive_apps_title" msgid="1317817863508274533">"Միացրած հավելվածներ"</string> +    <string name="inactive_apps_title" msgid="1317817863508274533">"Անգործուն հավելվածներ"</string>      <string name="inactive_app_inactive_summary" msgid="5091363706699855725">"Ակտիվ չէ: Հպեք՝ փոխելու համար:"</string>      <string name="inactive_app_active_summary" msgid="4174921824958516106">"Ակտիվ է: Հպեք՝ փոխելու համար:"</string>      <string name="runningservices_settings_title" msgid="8097287939865165213">"Աշխատեցվող ծառայություններ"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 733e2cba154b..e0157bc2922b 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -114,8 +114,8 @@      <string name="tts_default_lang_summary" msgid="5219362163902707785">"Menyetel suara spesifik bahasa untuk teks lisan"</string>      <string name="tts_play_example_title" msgid="7094780383253097230">"Dengarkan contoh"</string>      <string name="tts_play_example_summary" msgid="8029071615047894486">"Putar demonstrasi singkat dari sintesis suara"</string> -    <string name="tts_install_data_title" msgid="4264378440508149986">"Pasang data suara"</string> -    <string name="tts_install_data_summary" msgid="5742135732511822589">"Pasang data suara yang dibutuhkan untuk sintesis suara"</string> +    <string name="tts_install_data_title" msgid="4264378440508149986">"Instal data suara"</string> +    <string name="tts_install_data_summary" msgid="5742135732511822589">"Instal data suara yang dibutuhkan untuk sintesis suara"</string>      <string name="tts_engine_security_warning" msgid="8786238102020223650">"Mesin sintesis suara ini mungkin dapat mengumpulkan semua teks yang akan diucapkan, termasuk di antaranya data pribadi seperti sandi dan nomor kartu kredit. Berasal dari <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> aplikasi. Gunakan metode masukan ini?"</string>      <string name="tts_engine_network_required" msgid="1190837151485314743">"Bahasa ini perlu sambungan jaringan yang bekerja untuk keluaran text-to-speech."</string>      <string name="tts_default_sample_string" msgid="4040835213373086322">"Ini adalah contoh sintesis suara"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 14b8e3789c39..0ebe77e54991 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -101,7 +101,7 @@      <string name="unknown" msgid="1592123443519355854">"Sconosciuta"</string>      <string name="running_process_item_user_label" msgid="3129887865552025943">"Utente: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>      <string name="launch_defaults_some" msgid="313159469856372621">"Alcune opzioni predefinite impostate"</string> -    <string name="launch_defaults_none" msgid="4241129108140034876">"Nessuna applicazione predefinita impostata"</string> +    <string name="launch_defaults_none" msgid="4241129108140034876">"Nessuna app predefinita impostata"</string>      <string name="tts_settings" msgid="8186971894801348327">"Impostazioni di sintesi vocale"</string>      <string name="tts_settings_title" msgid="1237820681016639683">"Output sintesi vocale"</string>      <string name="tts_default_rate_title" msgid="6030550998379310088">"Velocità voce"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index d299ec6936e9..917415cac630 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -91,7 +91,7 @@      <string name="process_kernel_label" msgid="3916858646836739323">"ប្រព័ន្ធប្រតិបត្តិការ Android"</string>      <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"កម្មវិធីដែលបានលុប"</string>      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"បានលុបកម្មវិធី និងអ្នកប្រើ"</string> -    <string name="tether_settings_title_usb" msgid="6688416425801386511">"ការភ្ជាប់យូអេសប៊ី"</string> +    <string name="tether_settings_title_usb" msgid="6688416425801386511">"ការភ្ជាប់តាម USB"</string>      <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ហតស្ពតចល័ត"</string>      <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ការភ្ជាប់ប៊្លូធូស"</string>      <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ការភ្ជាប់"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index f38ef5d5a30e..88b36753e6c7 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -55,7 +55,7 @@      <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Користи за споделување контакти"</string>      <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Споделување конекција на интернет"</string>      <string name="bluetooth_profile_map" msgid="5465271250454324383">"Порака за пристап"</string> -    <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Пристап до СИМ"</string> +    <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Пристап до SIM"</string>      <string name="bluetooth_profile_a2dp_high_quality" msgid="2221025895896419505">"Користи висококвалитетно аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>      <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="5860996587596508755">"Користи висококвалитетно аудио"</string>      <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Поврзан со аудио на медиуми"</string> @@ -69,7 +69,7 @@      <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Споделување локална конекција на интернет со уред"</string>      <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Користи за пристап на интернет"</string>      <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Користи за карта"</string> -    <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Користете се пристап до СИМ"</string> +    <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Користете се пристап до SIM"</string>      <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Користи за аудио на медиуми"</string>      <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Користи за аудио на телефон"</string>      <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Користи за пренос на датотеки"</string> @@ -91,7 +91,7 @@      <string name="process_kernel_label" msgid="3916858646836739323">"Оперативен систем Android"</string>      <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Отстранети апликации"</string>      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Отстранети апликации и корисници"</string> -    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Поврзување со УСБ"</string> +    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Поврзување со USB"</string>      <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Преносл. точка на пристап"</string>      <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Поврзување со Bluetooth"</string>      <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Поврзување"</string> @@ -153,7 +153,7 @@      <string name="apn_settings_not_available" msgid="7873729032165324000">"Поставките за името на пристапната точка не се достапни за овој корисник"</string>      <string name="enable_adb" msgid="7982306934419797485">"Отстранување грешки на USB"</string>      <string name="enable_adb_summary" msgid="4881186971746056635">"Режим на отстранување грешки кога е поврзано USB"</string> -    <string name="clear_adb_keys" msgid="4038889221503122743">"Отповикај овластувања за отстранување грешки од УСБ"</string> +    <string name="clear_adb_keys" msgid="4038889221503122743">"Отповикај овластувања за отстранување грешки од USB"</string>      <string name="bugreport_in_power" msgid="7923901846375587241">"Кратенка за извештај за грешка"</string>      <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Прикажи копче во менито за вклучување за да се направи извештај за грешка"</string>      <string name="keep_screen_on" msgid="1146389631208760344">"Остани во активен режим"</string> @@ -198,18 +198,18 @@      <string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Кога веќе не го следиме постојаниот дневник, мора да ги избришеме податоците на дневникот што се наоѓаат на вашиот уред."</string>      <string name="select_logpersist_title" msgid="7530031344550073166">"Зачувувај податоци на дневникот"</string>      <string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Изберете привремена меморија на евиденција што ќе се користи постојано на уредот"</string> -    <string name="select_usb_configuration_title" msgid="2649938511506971843">"Изберете конфигурација за УСБ"</string> -    <string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Изберете конфигурација за УСБ"</string> +    <string name="select_usb_configuration_title" msgid="2649938511506971843">"Изберете конфигурација за USB"</string> +    <string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Изберете конфигурација за USB"</string>      <string name="allow_mock_location" msgid="2787962564578664888">"Овозможи лажни локации"</string>      <string name="allow_mock_location_summary" msgid="317615105156345626">"Овозможи лажни локации"</string>      <string name="debug_view_attributes" msgid="6485448367803310384">"Овозможете проверка на атрибутот на приказот"</string>      <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Секогаш држи го активен мобилниот интернет, дури и при активно Wi-Fi (за брзо префрлување мрежа)."</string> -    <string name="adb_warning_title" msgid="6234463310896563253">"Овозможи отстранување грешки на УСБ?"</string> -    <string name="adb_warning_message" msgid="7316799925425402244">"Отстранувањето грешки на УСБ е наменето само за целите на развој. Користете го за копирање податоци меѓу вашиот компјутер и вашиот уред, за инсталирање апликации на вашиот уред без известување и за читање евиденција на податоци."</string> -    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Отповикај пристап кон отстранување грешка од УСБ од сите претходно овластени компјутери?"</string> +    <string name="adb_warning_title" msgid="6234463310896563253">"Овозможи отстранување грешки на USB?"</string> +    <string name="adb_warning_message" msgid="7316799925425402244">"Отстранувањето грешки на USB е наменето само за целите на развој. Користете го за копирање податоци меѓу вашиот компјутер и вашиот уред, за инсталирање апликации на вашиот уред без известување и за читање евиденција на податоци."</string> +    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Отповикај пристап кон отстранување грешка од USB од сите претходно овластени компјутери?"</string>      <string name="dev_settings_warning_title" msgid="7244607768088540165">"Дозволи подесувања за развој?"</string>      <string name="dev_settings_warning_message" msgid="2298337781139097964">"Овие подесувања се наменети само за употреба за развој. Тие може да предизвикаат уредот и апликациите во него да се расипат или да се однесуваат необично."</string> -    <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Потврди апликации преку УСБ"</string> +    <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Потврди апликации преку USB"</string>      <string name="verify_apps_over_usb_summary" msgid="9164096969924529200">"Провери апликации инсталирани преку ADB/ADT за штетно однесување."</string>      <string name="bluetooth_disable_absolute_volume_summary" msgid="6031284410786545957">"Ја оневозможува карактеристиката за апсолутна јачина на звук преку Bluetooth во случај кога ќе настанат проблеми со далечинските уреди, како на пр., неприфатливо силен звук или недоволна контрола."</string>      <string name="bluetooth_enable_inband_ringing_summary" msgid="2787866074741784975">"Дозволи мелодиите на телефонот да се пуштаат на Bluetooth слушалките"</string> @@ -250,8 +250,8 @@      <string name="disable_overlays_summary" msgid="3578941133710758592">"Секогаш користи GPU за составување екран"</string>      <string name="simulate_color_space" msgid="6745847141353345872">"Симулирај простор на бои"</string>      <string name="enable_opengl_traces_title" msgid="6790444011053219871">"Овозможи траги на OpenGL"</string> -    <string name="usb_audio_disable_routing" msgid="8114498436003102671">"Исклучи УСБ-пренасочување"</string> -    <string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Исклучи автоматско пренасочување до УСБ-аудиоуреди"</string> +    <string name="usb_audio_disable_routing" msgid="8114498436003102671">"Исклучи USB-пренасочување"</string> +    <string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Исклучи автоматско пренасочување до USB-аудиоуреди"</string>      <string name="debug_layout" msgid="5981361776594526155">"Прикажи граници на слој"</string>      <string name="debug_layout_summary" msgid="2001775315258637682">"Прикажи граници на клип, маргини, итн."</string>      <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Сила на RTL за насока на слој"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 71945055ded0..41a116eddaf1 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -324,7 +324,7 @@      <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഏതാണ്ട് <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>      <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>      <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> -    <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന് <xliff:g id="TIME">%2$s</xliff:g>"</string> +    <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഫുൾ ചാർജാകാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="battery_info_status_unknown" msgid="196130600938058547">"അജ്ഞാതം"</string>      <string name="battery_info_status_charging" msgid="1705179948350365604">"ചാർജ്ജുചെയ്യുന്നു"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 21bc2dc86993..c2e8a94fe33d 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -82,7 +82,7 @@      <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ပင်နံပါတ် သို့မဟုတ် ဖြတ်သန်းခွင့်ကီးမမှန်ကန်သောကြောင့်<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ။"</string>      <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့်ဆက်သွယ်မရပါ"</string>      <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့်တွဲချိတ်ရန် ပယ်ချခံရသည်"</string> -    <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi  မရှိ"</string> +    <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi  ပိတ်ထားသည်"</string>      <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi  ချိတ်ဆက်ထားမှု မရှိပါ"</string>      <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi  ၁ ဘားရှိ"</string>      <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi  ၂ ဘား"</string> @@ -91,9 +91,9 @@      <string name="process_kernel_label" msgid="3916858646836739323">"Android စနစ်"</string>      <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ဖယ်ရှားထားသော အက်ပ်များ"</string>      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ဖယ်ရှားထားသော အပလီကေးရှင်းနှင့် သုံးစွဲသူများ"</string> -    <string name="tether_settings_title_usb" msgid="6688416425801386511">"USBမှတဆင့်ချိတ်ဆက်ခြင်း"</string> +    <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB သုံး၍ချိတ်ဆက်ခြင်း"</string>      <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ရွေ့လျားနိုင်သောဟော့စပေါ့"</string> -    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ဘလူးတုသ်တဆင့်ပြန်ချိတ်ဆက်"</string> +    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ဘလူးတုသ်သုံးချိတ်ဆက်ခြင်း"</string>      <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"တဆင့်ပြန်လည်ချိတ်ဆက်ခြင်း"</string>      <string name="tether_settings_title_all" msgid="8356136101061143841">"တဆင့်ချိတ်ဆက်ခြင်း၊ ဟော့စပေါ့"</string>      <string name="managed_user_title" msgid="8109605045406748842">"အလုပ်သုံးအက်ပ်များအားလုံး"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 8738e042982c..2f61e8c0cb45 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -103,7 +103,7 @@      <string name="launch_defaults_some" msgid="313159469856372621">"केही पूर्वनिर्धारितहरू सेट गरिएका छन्"</string>      <string name="launch_defaults_none" msgid="4241129108140034876">"कुनै पूर्वनिर्धारित सेट गरिएको छैन"</string>      <string name="tts_settings" msgid="8186971894801348327">"पाठ-वाचन सेटिङहरू"</string> -    <string name="tts_settings_title" msgid="1237820681016639683">"पाठ-बाट-वाणी उत्पादन"</string> +    <string name="tts_settings_title" msgid="1237820681016639683">"पाठवाचकको उत्पादन"</string>      <string name="tts_default_rate_title" msgid="6030550998379310088">"वाणी दर"</string>      <string name="tts_default_rate_summary" msgid="4061815292287182801">"पाठ वाचन हुने गति"</string>      <string name="tts_default_pitch_title" msgid="6135942113172488671">"पिच"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 30cd8db040ab..77e311eefb22 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -115,7 +115,7 @@      <string name="tts_play_example_title" msgid="7094780383253097230">"Luisteren naar een voorbeeld"</string>      <string name="tts_play_example_summary" msgid="8029071615047894486">"Een korte demonstratie van spraaksynthese afspelen"</string>      <string name="tts_install_data_title" msgid="4264378440508149986">"Spraakgegevens installeren"</string> -    <string name="tts_install_data_summary" msgid="5742135732511822589">"De stemgegevens voor spraaksynthese installeren"</string> +    <string name="tts_install_data_summary" msgid="5742135732511822589">"De spraakgegevens voor spraaksynthese installeren"</string>      <string name="tts_engine_security_warning" msgid="8786238102020223650">"Deze engine voor spraaksynthese kan mogelijk alle tekst verzamelen die wordt gesproken, waaronder persoonlijke gegevens zoals wachtwoorden en creditcardnummers. Deze engine is afkomstig van de <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>-engine. Het gebruik van deze engine voor spraaksynthese inschakelen?"</string>      <string name="tts_engine_network_required" msgid="1190837151485314743">"Deze taal heeft een werkende netwerkverbinding nodig voor tekst-naar-spraak-uitvoer."</string>      <string name="tts_default_sample_string" msgid="4040835213373086322">"Dit is een voorbeeld van spraaksynthese"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 1e55e155bdf6..8534e8568013 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -91,9 +91,9 @@      <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>      <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ਹਟਾਏ ਗਏ ਐਪਸ"</string>      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ਹਟਾਏ ਗਏ ਐਪਸ ਅਤੇ ਉਪਭੋਗਤਾ"</string> -    <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੀਥਰਿੰਗ"</string> +    <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੈਦਰਿੰਗ"</string>      <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string> -    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth ਟੀਥਰਿੰਗ"</string> +    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth ਟੈਦਰਿੰਗ"</string>      <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"ਟੀਥਰਿੰਗ"</string>      <string name="tether_settings_title_all" msgid="8356136101061143841">"ਟੀਥਰਿੰਗ & ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string>      <string name="managed_user_title" msgid="8109605045406748842">"ਸਾਰੀਆਂ ਕੰਮ ਐਪਾਂ"</string> @@ -119,7 +119,7 @@      <string name="tts_engine_security_warning" msgid="8786238102020223650">"ਇਹ ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਇੰਜਣ ਉਹ ਸਾਰਾ ਟੈਕਸਟ ਇਕੱਤਰ ਕਰਨ ਵਿੱਚ ਸਮਰੱਥ ਹੋ ਸਕਦਾ ਹੈ, ਜੋ ਬੋਲਿਆ ਜਾਏਗਾ, ਨਿੱਜੀ ਡੈਟਾ ਸਮੇਤ ਜਿਵੇਂ ਪਾਸਵਰਡ ਅਤੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ। ਇਹ <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ਇੰਜਣ ਤੋਂ ਆਉਂਦਾ ਹੈ। ਕੀ ਇਸ ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਇੰਜਣ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>      <string name="tts_engine_network_required" msgid="1190837151485314743">"ਇਸ ਭਾਸ਼ਾ ਲਈ ਟੈਕਸਟ-ਟੂ-ਸਪੀਚ ਆਊਟਪੁਟ ਲਈ ਇੱਕ ਚਾਲੂ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਦੀ ਲੋੜ ਹੈ।"</string>      <string name="tts_default_sample_string" msgid="4040835213373086322">"ਇਹ ਸਪੀਚ ਸਿੰਥੈਸਿਸ ਦਾ ਇੱਕ ਉਦਾਹਰਨ ਹੈ"</string> -    <string name="tts_status_title" msgid="7268566550242584413">"ਡਿਫੌਲਟ ਭਾਸ਼ਾ ਸਥਿਤੀ"</string> +    <string name="tts_status_title" msgid="7268566550242584413">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਭਾਸ਼ਾ ਸਥਿਤੀ"</string>      <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਮਰਥਿਤ ਹੈ"</string>      <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> ਲਈ ਨੈੱਟਵਰਕ ਕਨੈਕਸ਼ਨ ਲੁੜੀਂਦਾ ਹੈ"</string>      <string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string> @@ -285,7 +285,7 @@      <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"ਨਵਾਂ ਪਾਸਵਰਡ ਅਤੇ ਪੁਸ਼ਟੀ ਮੇਲ ਨਹੀਂ ਖਾਂਦੀ"</string>      <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"ਬੈਕਅਪ ਪਾਸਵਰਡ ਸੈਟ ਕਰਨ ਵਿੱਚ ਅਸਫਲਤਾ"</string>    <string-array name="color_mode_names"> -    <item msgid="2425514299220523812">"ਚਮਕੀਲਾ (ਡਿਫੌਲਟ)"</item> +    <item msgid="2425514299220523812">"ਚਮਕੀਲਾ (ਪੂਰਵ-ਨਿਰਧਾਰਤ)"</item>      <item msgid="8446070607501413455">"ਕੁਦਰਤੀ"</item>      <item msgid="6553408765810699025">"ਸਟੈਂਡਰਡ"</item>    </string-array> @@ -306,8 +306,8 @@      <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ਤਬਦੀਲ ਕਰੋ ..."</string>      <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਤੋਂ ਇਨਕ੍ਰਿਪਟਡ ਹੈ"</string>      <string name="title_convert_fbe" msgid="1263622876196444453">"ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> -    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"ਡੈਟਾ ਪਾਰਟੀਸ਼ਨ ਨੂੰ ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰੋ\n !!ਚੇਤਾਵਨੀ!! ਇਹ ਤੁਹਾਡੇ ਸਾਰੇ ਡੈਟੇ ਨੂੰ ਸਾਫ਼ ਕਰ ਦੇਵੇਗਾ\n ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਅਲਫਾ ਹੈ, ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।\n ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਮਿਟਾਓ ਅਤੇ ਤਬਦੀਲ ਕਰੋ...\' ਨੂੰ ਦਬਾਓ।"</string> -    <string name="button_convert_fbe" msgid="5152671181309826405">"ਮਿਟਾਓ ਅਤੇ ਤਬਦੀਲ ਕਰੋ..."</string> +    <string name="convert_to_fbe_warning" msgid="6139067817148865527">"ਡੈਟਾ ਪਾਰਟੀਸ਼ਨ ਦਾ ਫ਼ਾਈਲ ਆਧਾਰਿਤ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਰੁਪਾਂਤਰਣ ਕਰੋ\n !!ਚੇਤਾਵਨੀ!! ਇਹ ਤੁਹਾਡੇ ਸਾਰੇ ਡੈਟੇ ਨੂੰ ਮਿਟਾ ਦੇਵੇਗਾ\n ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਿਕ ਹੈ, ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।\n ਜਾਰੀ ਰੱਖਣ ਲਈ \'ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ...\' ਨੂੰ ਦਬਾਓ।"</string> +    <string name="button_convert_fbe" msgid="5152671181309826405">"ਮਿਟਾਓ ਅਤੇ ਰੁਪਾਂਤਰਣ ਕਰੋ..."</string>      <string name="picture_color_mode" msgid="4560755008730283695">"ਤਸਵੀਰ ਰੰਗ ਮੋਡ"</string>      <string name="picture_color_mode_desc" msgid="1141891467675548590">"sRGB ਵਰਤੋਂ ਕਰੋ"</string>      <string name="daltonizer_mode_disabled" msgid="7482661936053801862">"ਅਯੋਗ ਬਣਾਇਆ"</string> @@ -345,9 +345,9 @@      <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ਬਾਕੀ"</string>      <string name="screen_zoom_summary_small" msgid="5867245310241621570">"ਛੋਟਾ"</string>      <string name="screen_zoom_summary_default" msgid="2247006805614056507">"ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string> -    <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ਵੱਡਾ"</string> -    <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ਥੋੜ੍ਹਾ ਵੱਡਾ"</string> -    <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ਸਭ ਤੋਂ ਵੱਡਾ"</string> +    <string name="screen_zoom_summary_large" msgid="4835294730065424084">"ਵੱਡੀ"</string> +    <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ਥੋੜ੍ਹਾ ਵੱਡੀ"</string> +    <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ਸਭ ਤੋਂ ਵੱਡੀ"</string>      <string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>      <string name="help_feedback_label" msgid="6815040660801785649">"ਮਦਦ ਅਤੇ ਪ੍ਰਤੀਕਰਮ"</string>      <string name="content_description_menu_button" msgid="8182594799812351266">"ਮੀਨੂ"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index fead9f073bd4..80b899004a60 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -31,7 +31,7 @@      <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>      <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não se conectará automaticamente"</string>      <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string> -    <string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string> +    <string name="saved_network" msgid="4352716707126620811">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string>      <string name="connected_via_network_scorer" msgid="5713793306870815341">"Conectado automaticamente via %1$s"</string>      <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Conectado automaticamente via provedor de avaliação de rede"</string>      <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index fead9f073bd4..80b899004a60 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -31,7 +31,7 @@      <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>      <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não se conectará automaticamente"</string>      <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string> -    <string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string> +    <string name="saved_network" msgid="4352716707126620811">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string>      <string name="connected_via_network_scorer" msgid="5713793306870815341">"Conectado automaticamente via %1$s"</string>      <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Conectado automaticamente via provedor de avaliação de rede"</string>      <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 9504a737eba9..0ae7049ab002 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -324,7 +324,7 @@      <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - încă aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – timp rămas: <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> -    <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă"</string> +    <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcarea completă"</string>      <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>      <string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string>      <string name="battery_info_status_charging" msgid="1705179948350365604">"Încarcă"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 75b766ba1ee3..028d561e7c00 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -344,7 +344,7 @@      <string name="charge_length_format" msgid="8978516217024434156">"pred <xliff:g id="ID_1">%1$s</xliff:g>"</string>      <string name="remaining_length_format" msgid="7886337596669190587">"Zostáva <xliff:g id="ID_1">%1$s</xliff:g>"</string>      <string name="screen_zoom_summary_small" msgid="5867245310241621570">"Malé"</string> -    <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predvolená"</string> +    <string name="screen_zoom_summary_default" msgid="2247006805614056507">"Predvolené"</string>      <string name="screen_zoom_summary_large" msgid="4835294730065424084">"Veľké"</string>      <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Väčšie"</string>      <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najväčšie"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 8b24ef77620b..a6ad468425ac 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -106,7 +106,7 @@      <string name="tts_settings_title" msgid="1237820681016639683">"Kubadilisha maandishi hadi usemi"</string>      <string name="tts_default_rate_title" msgid="6030550998379310088">"Kasi ya kutamka"</string>      <string name="tts_default_rate_summary" msgid="4061815292287182801">"Kasi ya kutamkwa kwa maneno"</string> -    <string name="tts_default_pitch_title" msgid="6135942113172488671">"Giza"</string> +    <string name="tts_default_pitch_title" msgid="6135942113172488671">"Uzito wa sauti"</string>      <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Huathiri sauti ya matamshi yaliyounganishwa"</string>      <string name="tts_default_lang_title" msgid="8018087612299820556">"Lugha"</string>      <string name="tts_lang_use_system" msgid="2679252467416513208">"Tumia lugha ya mfumo"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 34dd8632d8b9..910e66c8bcf6 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -91,9 +91,9 @@      <string name="process_kernel_label" msgid="3916858646836739323">"Hệ điều hành Android"</string>      <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Ứng dụng đã xóa"</string>      <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Ứng dụng và người dùng bị xóa"</string> -    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Truy cập Internet qua USB"</string> +    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Chia sẻ kết nối Internet qua USB"</string>      <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Điểm phát sóng di động"</string> -    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Truy cập Internet qua Bluetooth"</string> +    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Chia sẻ kết nối Internet qua Bluetooth"</string>      <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Đang dùng làm điểm truy cập Internet"</string>      <string name="tether_settings_title_all" msgid="8356136101061143841">"USB Internet & điểm truy cập di động"</string>      <string name="managed_user_title" msgid="8109605045406748842">"Tất cả ứng dụng làm việc"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index ac7cd4ee158e..b224c23a7400 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -313,7 +313,7 @@      <string name="daltonizer_mode_disabled" msgid="7482661936053801862">"已停用"</string>      <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"全色盲"</string>      <string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"绿色弱视(红绿不分)"</string> -    <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"红色弱视(红绿色)"</string> +    <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"红色弱视(红绿不分)"</string>      <string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"蓝色弱视(蓝黄色)"</string>      <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>      <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"这是实验性功能,性能可能不稳定。"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 484f1d38e054..03081af1b1f1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -31,7 +31,7 @@      <string name="wifi_not_in_range" msgid="1136191511238508967">"超出可用範圍"</string>      <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"不會自動連線"</string>      <string name="wifi_no_internet" msgid="3880396223819116454">"無法偵測互聯網連線"</string> -    <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> 的儲存"</string> +    <string name="saved_network" msgid="4352716707126620811">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>      <string name="connected_via_network_scorer" msgid="5713793306870815341">"已透過 %1$s 自動連線"</string>      <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"已透過網絡評分供應商自動連線"</string>      <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string> @@ -316,7 +316,7 @@      <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"紅色弱視 (紅綠)"</string>      <string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"藍色弱視 (藍黃)"</string>      <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string> -    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會影響效能。"</string> +    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是實驗性功能,效能尚待改善。"</string>      <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>      <string name="power_remaining_duration_only" msgid="845431008899029842">"剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>      <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g>後就能充滿電"</string> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java index 4a7d0fd8d1ff..28f78e58c05a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java @@ -96,4 +96,7 @@ public interface NotificationMenuRowPlugin extends Plugin {      public default boolean useDefaultMenuItems() {          return false;      } + +    public default void onConfigurationChanged() { +    }  } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 93687478fc86..21bf46248be8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -24,6 +24,8 @@ import android.animation.ObjectAnimator;  import android.animation.ValueAnimator.AnimatorUpdateListener;  import android.annotation.Nullable;  import android.content.Context; +import android.content.res.Resources; +import android.content.res.Configuration;  import android.graphics.drawable.AnimatedVectorDrawable;  import android.graphics.drawable.AnimationDrawable;  import android.graphics.drawable.ColorDrawable; @@ -84,6 +86,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView      private static final int COLORED_DIVIDER_ALPHA = 0x7B;      private static final int MENU_VIEW_INDEX = 0; +    public interface LayoutListener { +        public void onLayout(); +    } + +    private LayoutListener mLayoutListener;      private final NotificationInflater mNotificationInflater;      private int mIconTransformContentShift;      private int mIconTransformContentShiftNoIcon; @@ -834,6 +841,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView          onNotificationUpdated();      } +    @Override +    public void onConfigurationChanged(Configuration newConfig) { +        if (mMenuRow.getMenuView() != null) { +            mMenuRow.onConfigurationChanged(); +        } +    } +      public void setContentBackground(int customBackgroundColor, boolean animate,              NotificationContentView notificationContentView) {          if (getShowingLayout() == notificationContentView) { @@ -1580,6 +1594,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView          mIsSystemChildExpanded = expanded;      } +    public void setLayoutListener(LayoutListener listener) { +        mLayoutListener = listener; +    } + +    public void removeListener() { +        mLayoutListener = null; +    } +      @Override      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {          super.onLayout(changed, left, top, right, bottom); @@ -1588,6 +1610,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView              mMenuRow.onHeightUpdate();          }          updateContentShiftHeight(); +        if (mLayoutListener != null) { +            mLayoutListener.onLayout(); +        }      }      /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java index dc538dac6856..d4b892064980 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java @@ -45,7 +45,8 @@ import android.view.ViewGroup;  import android.widget.FrameLayout;  import android.widget.FrameLayout.LayoutParams; -public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener { +public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnClickListener, +        ExpandableNotificationRow.LayoutListener {      private static final boolean DEBUG = false;      private static final String TAG = "swipe"; @@ -166,6 +167,18 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl          createMenuViews();      } +    @Override +    public void onConfigurationChanged() { +        mParent.setLayoutListener(this); +    } + +    @Override +    public void onLayout() { +        mIconsPlaced = false; // Force icons to be re-placed +        setMenuLocation(); +        mParent.removeListener(); +    } +      private void createMenuViews() {          // Filter the menu items based on the notification          if (mParent != null && mParent.getStatusBarNotification() != null) { @@ -460,22 +473,17 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl      private void setMenuLocation() {          boolean showOnLeft = mTranslation > 0; -        if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping || mParent == null) { +        if ((mIconsPlaced && showOnLeft == mOnLeft) || mSnapping +                || !mMenuContainer.isAttachedToWindow()) {              // Do nothing              return;          } -        final boolean isRtl = mParent.isLayoutRtl();          final int count = mMenuContainer.getChildCount(); -        final int width = mParent.getWidth();          for (int i = 0; i < count; i++) {              final View v = mMenuContainer.getChildAt(i); -            final float left = isRtl -                    ? -(width - mHorizSpaceForIcon * (i + 1)) -                    : i * mHorizSpaceForIcon; -            final float right = isRtl -                    ? -i * mHorizSpaceForIcon -                    : width - (mHorizSpaceForIcon * (i + 1)); -            v.setTranslationX(showOnLeft ? left : right); +            final float left = i * mHorizSpaceForIcon; +            final float right = mParent.getWidth() - (mHorizSpaceForIcon * (i + 1)); +            v.setX(showOnLeft ? left : right);          }          mOnLeft = showOnLeft;          mIconsPlaced = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index b134fc542776..f41670e0bdce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -346,7 +346,8 @@ public class NotificationShelf extends ActivatableNotificationView implements          }          float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;          if (clampedAmount == fullTransitionAmount) { -            iconState.useFullTransitionAmount = scrollingFast || expandingAnimated +            iconState.noAnimations = scrollingFast || expandingAnimated; +            iconState.useFullTransitionAmount = iconState.noAnimations                  || (!ICON_ANMATIONS_WHILE_SCROLLING && fullTransitionAmount == 0.0f && scrolling);              iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING                      && fullTransitionAmount == 0.0f && !mAmbientState.isExpansionChanging(); @@ -357,6 +358,7 @@ public class NotificationShelf extends ActivatableNotificationView implements                  && !ViewState.isAnimatingY(icon))) {              iconState.cancelAnimations(icon);              iconState.useFullTransitionAmount = true; +            iconState.noAnimations = true;          }          float transitionAmount;          if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 14c1606762af..930191e6d51d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -466,6 +466,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {          public boolean useLinearTransitionAmount;          public boolean translateContent;          public int iconColor = StatusBarIconView.NO_COLOR; +        public boolean noAnimations;          @Override          public void applyToView(View view) { @@ -473,7 +474,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {                  StatusBarIconView icon = (StatusBarIconView) view;                  boolean animate = false;                  AnimationProperties animationProperties = null; -                boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation; +                boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation +                        && !noAnimations;                  if (animationsAllowed) {                      if (justAdded) {                          super.applyToView(icon); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index e3398c96d881..4dd0b35b6d6b 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -454,10 +454,12 @@ public final class AutofillManagerService extends SystemService {          }          @Override -        public void setAuthenticationResult(Bundle data, int sessionId, int userId) { +        public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId, +                int userId) {              synchronized (mLock) {                  final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); -                service.setAuthenticationResultLocked(data, sessionId, getCallingUid()); +                service.setAuthenticationResultLocked(data, sessionId, authenticationId, +                        getCallingUid());              }          } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index e315f9d15a74..faa61825dc71 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -241,13 +241,13 @@ final class AutofillManagerServiceImpl {          return isEnabled();      } -    void setAuthenticationResultLocked(Bundle data, int sessionId, int uid) { +    void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {          if (!isEnabled()) {              return;          }          final Session session = mSessions.get(sessionId);          if (session != null && uid == session.uid) { -            session.setAuthenticationResultLocked(data); +            session.setAuthenticationResultLocked(data, authenticationId);          }      } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 3f78fb8caea7..70771e866684 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -29,7 +29,6 @@ import static com.android.server.autofill.Helper.findViewNodeById;  import static com.android.server.autofill.Helper.sDebug;  import static com.android.server.autofill.Helper.sVerbose;  import static com.android.server.autofill.ViewState.STATE_AUTOFILLED; -import static com.android.server.autofill.ViewState.STATE_FILLABLE;  import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;  import android.annotation.NonNull; @@ -58,7 +57,6 @@ import android.service.autofill.FillResponse;  import android.service.autofill.SaveInfo;  import android.service.autofill.SaveRequest;  import android.util.ArrayMap; -import android.util.DebugUtils;  import android.util.Slog;  import android.util.SparseArray;  import android.view.autofill.AutofillId; @@ -79,6 +77,7 @@ import com.android.server.autofill.ui.AutoFillUI;  import java.io.PrintWriter;  import java.util.ArrayList;  import java.util.Collections; +import java.util.List;  import java.util.Map;  import java.util.Map.Entry;  import java.util.concurrent.atomic.AtomicInteger; @@ -140,18 +139,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState      private SparseArray<FillResponse> mResponses;      /** -     * Response that requires a service authentitcation request. -     */ -    @GuardedBy("mLock") -    private FillResponse mResponseWaitingAuth; - -    /** -     * Dataset that when tapped launched a service authentication request. -     */ -    @GuardedBy("mLock") -    private Dataset mDatasetWaitingAuth; - -    /**       * Contexts read from the app; they will be updated (sanitized, change values for save) before       * sent to {@link AutofillService}. Ordered by the time they we read.       */ @@ -414,10 +401,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              notifyUnavailableToClient();          }          synchronized (mLock) { -            if (response.getAuthentication() != null) { -                // TODO(b/37424539): proper implementation -                mResponseWaitingAuth = response; -            }              processResponseLocked(response);          } @@ -525,7 +508,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState      // FillServiceCallbacks      @Override -    public void authenticate(int requestId, IntentSender intent, Bundle extras) { +    public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras) {          final Intent fillInIntent;          synchronized (mLock) {              synchronized (mLock) { @@ -541,7 +524,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          mService.setAuthenticationSelected(); -        mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent)); +        final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); +        mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId, +                intent, fillInIntent));      }      // FillServiceCallbacks @@ -552,7 +537,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState      // AutoFillUiCallback      @Override -    public void fill(int requestId, Dataset dataset) { +    public void fill(int requestId, int datasetIndex, Dataset dataset) {          synchronized (mLock) {              if (mDestroyed) {                  Slog.w(TAG, "Call to Session#fill() rejected - session: " @@ -560,7 +545,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  return;              }          } -        mHandlerCaller.getHandler().post(() -> autoFill(requestId, dataset)); +        mHandlerCaller.getHandler().post(() -> autoFill(requestId, datasetIndex, dataset));      }      // AutoFillUiCallback @@ -656,54 +641,42 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          });      } -    void setAuthenticationResultLocked(Bundle data) { +    void setAuthenticationResultLocked(Bundle data, int authenticationId) {          if (mDestroyed) {              Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "                      + id + " destroyed");              return;          } -        if ((mResponseWaitingAuth == null && mDatasetWaitingAuth == null) || data == null) { + +        final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId); +        final FillResponse authenticatedResponse = mResponses.get(requestId); +        if (authenticatedResponse == null || data == null) {              removeSelf(); -        } else { -            final Parcelable result = data.getParcelable( -                    AutofillManager.EXTRA_AUTHENTICATION_RESULT); -            if (sVerbose) Slog.d(TAG, "setAuthenticationResultLocked() for " + result); - -            if (result instanceof FillResponse) { -                FillResponse response = (FillResponse) result; - -                mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName); -                final int requestIndex = mResponses.indexOfValue(mResponseWaitingAuth); -                mResponseWaitingAuth = null; -                if (requestIndex >= 0) { -                    response.setRequestId(mResponses.keyAt(requestIndex)); -                    if (response.getDatasets() == null || response.getDatasets().isEmpty()) { -                        // TODO(b/37424539): there is a race condition that causes the authentication -                        // dialog to be shown again after the service authreplied with a no-datasets -                        // response. We're fixing it by hiding the UI when that happens, but that -                        // sounds like a hack - hopefully the real problem will go away when we -                        // refactor auth to support partitions; if it doesn't, we need to -                        // investigate it further (it can be reproduced by running -                        // LoginActivityTest.testFillResponseAuthServiceHasNoData()) -                        mUi.hideAll(this); -                    } -                    processResponseLocked(response); -                } else { -                    Slog.e(TAG, "Error cannot find id for auth response"); -                } -            } else if (result instanceof Dataset) { +            return; +        } + +        final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId( +                authenticationId); +        // Authenticated a dataset - reset view state regardless if we got a response or a dataset +        if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { +            final Dataset dataset = authenticatedResponse.getDatasets().get(datasetIdx); +            if (dataset == null) { +                removeSelf(); +                return; +            } +            resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH); +        } + +        final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); +        if (result instanceof FillResponse) { +            final FillResponse response = (FillResponse) result; +            mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName); +            replaceResponseLocked(authenticatedResponse, response); +        } else if (result instanceof Dataset) { +            if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {                  final Dataset dataset = (Dataset) result; -                for (int i = 0; i < mResponses.size(); i++) { -                    final FillResponse response = mResponses.valueAt(i); -                    final int index = response.getDatasets().indexOf(mDatasetWaitingAuth); -                    if (index >= 0) { -                        response.getDatasets().set(index, dataset); -                        mDatasetWaitingAuth = null; -                        autoFill(mResponses.keyAt(i), dataset); -                        resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH); -                        return; -                    } -                } +                authenticatedResponse.getDatasets().set(datasetIdx, dataset); +                autoFill(requestId, datasetIdx, dataset);              }          }      } @@ -939,6 +912,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          }      } +    private static final int PARTITION_MAX_COUNT = 64;      /**       * Determines if a new partition should be started for an id.       * @@ -952,6 +926,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          }          final int numResponses = mResponses.size(); +        if (numResponses >= PARTITION_MAX_COUNT) { +            Slog.e(TAG, "Cannot create more than 64 partitions. Not creating a new partition."); +            return false; +        } +          for (int responseNum = 0; responseNum < numResponses; responseNum++) {              final FillResponse response = mResponses.valueAt(responseNum); @@ -1056,7 +1035,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  // Remove the UI if the ViewState has changed.                  if (mCurrentViewId != viewState.id) { -                    mUi.hideFillUi(this); +                    hideFillUiIfOwnedByMe();                      mCurrentViewId = viewState.id;                  } @@ -1066,7 +1045,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              case ACTION_VIEW_EXITED:                  if (mCurrentViewId == viewState.id) {                      if (sVerbose) Slog.d(TAG, "Exiting view " + id); -                    mUi.hideFillUi(this); +                    hideFillUiIfOwnedByMe();                      mCurrentViewId = null;                  }                  break; @@ -1162,27 +1141,43 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          }      } -    private void processResponseLocked(@NonNull FillResponse response) { -        final int requestId = response.getRequestId(); +    private void replaceResponseLocked(@NonNull FillResponse oldResponse, +            @NonNull FillResponse newResponse) { +        // Disassociate view states with the old response +        setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, true); +        // Move over the id +        newResponse.setRequestId(oldResponse.getRequestId()); +        // Replace the old response +        mResponses.put(newResponse.getRequestId(), newResponse); +        // Now process the new response +        processResponseLocked(newResponse); +    } + +    private void processResponseLocked(@NonNull FillResponse newResponse) { +        // Make sure we are hiding the UI which will be shown +        // only if handling the current response requires it. +        hideAllUiIfOwnedByMe(); + +        final int requestId = newResponse.getRequestId();          if (sVerbose) {              Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId -                    + ", reqId=" + requestId + ", resp=" + response); +                    + ", reqId=" + requestId + ", resp=" + newResponse);          }          if (mResponses == null) {              mResponses = new SparseArray<>(4);          } -        mResponses.put(requestId, response); -        mClientState = response.getClientState(); +        mResponses.put(requestId, newResponse); +        mClientState = newResponse.getClientState(); -        setViewStatesLocked(response, ViewState.STATE_FILLABLE); +        setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);          updateTrackedIdsLocked();          if (mCurrentViewId == null) {              return;          } -        final ArrayList<Dataset> datasets = response.getDatasets(); +        final ArrayList<Dataset> datasets = newResponse.getDatasets();          if (datasets != null && datasets.size() == 1) {              // Check if it its a single response for a manual request, in which case it should @@ -1190,7 +1185,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              final FillContext context = getFillContextByRequestIdLocked(requestId);              if (context != null && (context.getStructure().getFlags() & FLAG_MANUAL_REQUEST) != 0) {                  Slog.d(TAG, "autofilling manual request directly"); -                autoFill(requestId, datasets.get(0)); +                autoFill(requestId, 0, datasets.get(0));                  return;              }          } @@ -1202,7 +1197,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState      /**       * Sets the state of all views in the given response.       */ -    private void setViewStatesLocked(FillResponse response, int state) { +    private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse) {          final ArrayList<Dataset> datasets = response.getDatasets();          if (datasets != null) {              for (int i = 0; i < datasets.size(); i++) { @@ -1211,7 +1206,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                      Slog.w(TAG, "Ignoring null dataset on " + datasets);                      continue;                  } -                setViewStatesLocked(response, dataset, state); +                setViewStatesLocked(response, dataset, state, clearResponse); +            } +        } else if (response.getAuthentication() != null) { +            for (AutofillId autofillId : response.getAuthenticationIds()) { +                final ViewState viewState = createOrUpdateViewStateLocked(autofillId, state, null); +                if (!clearResponse) { +                    viewState.setResponse(response); +                } else { +                    viewState.setResponse(null); +                }              }          }          final SaveInfo saveInfo = response.getSaveInfo(); @@ -1234,14 +1238,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  createOrUpdateViewStateLocked(id, state, null);              }          } -      }      /**       * Sets the state of all views in the given dataset and response.       */      private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset, -            int state) { +            int state, boolean clearResponse) {          final ArrayList<AutofillId> ids = dataset.getFieldIds();          final ArrayList<AutofillValue> values = dataset.getFieldValues();          for (int j = 0; j < ids.size(); j++) { @@ -1250,11 +1253,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              final ViewState viewState = createOrUpdateViewStateLocked(id, state, value);              if (response != null) {                  viewState.setResponse(response); +            } else if (clearResponse) { +                viewState.setResponse(null);              }          }      } -    private ViewState createOrUpdateViewStateLocked(AutofillId id, int state, AutofillValue value) { +    private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state, +            @Nullable AutofillValue value) {          ViewState viewState = mViewStates.get(id);          if (viewState != null)  {              viewState.setState(state); @@ -1285,7 +1291,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          }      } -    void autoFill(int requestId, Dataset dataset) { +    void autoFill(int requestId, int datasetIndex, Dataset dataset) {          synchronized (mLock) {              if (mDestroyed) {                  Slog.w(TAG, "Call to Session#autoFill() rejected - session: " @@ -1303,11 +1309,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              // ...or handle authentication.              // TODO(b/37424539): proper implementation              mService.setDatasetAuthenticationSelected(dataset.getId()); -            mDatasetWaitingAuth = dataset; -            setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH); +            setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);              final Intent fillInIntent = createAuthFillInIntent( -                    getFillContextByRequestIdLocked(requestId).getStructure(), null); -            startAuthentication(dataset.getAuthentication(), fillInIntent); +                    getFillContextByRequestIdLocked(requestId).getStructure(), mClientState); + +            final int authenticationId = AutofillManager.makeAuthenticationId(requestId, +                    datasetIndex); +            startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent);          }      } @@ -1317,25 +1325,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          }      } -    FillResponse getResponseWaitingAuth() { -        synchronized (mLock) { -            return mResponseWaitingAuth; -        } -    } -      private Intent createAuthFillInIntent(AssistStructure structure, Bundle extras) {          final Intent fillInIntent = new Intent();          fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, structure); -        if (extras != null) { -            fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras); -        } +        fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras);          return fillInIntent;      } -    private void startAuthentication(IntentSender intent, Intent fillInIntent) { +    private void startAuthentication(int authenticationId, IntentSender intent, +            Intent fillInIntent) {          try {              synchronized (mLock) { -                mClient.authenticate(id, intent, fillInIntent); +                mClient.authenticate(id, authenticationId, intent, fillInIntent);              }          } catch (RemoteException e) {              Slog.e(TAG, "Error launching auth intent", e); @@ -1347,8 +1348,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          pw.print(prefix); pw.print("uid: "); pw.println(uid);          pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);          pw.print(prefix); pw.print("mResponses: "); pw.println(mResponses); -        pw.print(prefix); pw.print("mResponseWaitingAuth: "); pw.println(mResponseWaitingAuth); -        pw.print(prefix); pw.print("mDatasetWaitingAuth: "); pw.println(mDatasetWaitingAuth);          pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);          pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());          pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); @@ -1392,8 +1391,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              }              try {                  if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); -                mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues()); -                setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED); +                // Skip null values as a null values means no change +                final int entryCount = dataset.getFieldIds().size(); +                final List<AutofillId> ids = new ArrayList<>(entryCount); +                final List<AutofillValue> values = new ArrayList<>(entryCount); +                for (int i = 0; i < entryCount; i++) { +                    if (dataset.getFieldValues().get(i) == null) { +                        continue; +                    } +                    ids.add(dataset.getFieldIds().get(i)); +                    values.add(dataset.getFieldValues().get(i)); +                } +                if (!ids.isEmpty()) { +                    mClient.autofill(id, ids, values); +                } +                setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, false);              } catch (RemoteException e) {                  Slog.w(TAG, "Error autofilling activity: " + e);              } @@ -1412,12 +1424,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              return;          }          mRemoteFillService.destroy(); -        mUi.hideAll(this); +        hideAllUiIfOwnedByMe();          mUi.clearCallback(this);          mDestroyed = true;          mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);      } +    private void hideAllUiIfOwnedByMe() { +        mUi.hideAll(this); +    } + +    private void hideFillUiIfOwnedByMe() { +        mUi.hideFillUi(this); +    } +      private void removeSelf() {          synchronized (mLock) {              removeSelfLocked(); diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index 561c603ea3b0..f87fa1970c4a 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -112,7 +112,7 @@ final class ViewState {          return mAutofilledValue;      } -    void setAutofilledValue(AutofillValue value) { +    void setAutofilledValue(@Nullable AutofillValue value) {          mAutofilledValue = value;      } @@ -180,15 +180,9 @@ final class ViewState {          }          // First try the current response associated with this View.          if (mResponse != null) { -            if (mResponse.getDatasets() != null) { +            if (mResponse.getDatasets() != null || mResponse.getAuthentication() != null) {                  mListener.onFillReady(mResponse, this.id, mCurrentValue);              } -            return; -        } -        // Then checks if the session has a response waiting authentication; if so, uses it instead. -        final FillResponse responseWaitingAuth = mSession.getResponseWaitingAuth(); -        if (responseWaitingAuth != null) { -            mListener.onFillReady(responseWaitingAuth, this.id, mCurrentValue);          }      } diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 9eaabfe23b1b..4f69f644f5e8 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -30,6 +30,7 @@ import android.service.autofill.SaveInfo;  import android.text.TextUtils;  import android.util.Slog;  import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager;  import android.view.autofill.IAutofillWindowPresenter;  import android.widget.Toast; @@ -60,8 +61,9 @@ public final class AutoFillUI {      private final MetricsLogger mMetricsLogger = new MetricsLogger();      public interface AutoFillUiCallback { -        void authenticate(int requestId, @NonNull IntentSender intent, @Nullable Bundle extras); -        void fill(int requestId, @NonNull Dataset dataset); +        void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent, +                @Nullable Bundle extras); +        void fill(int requestId, int datasetIndex, @NonNull Dataset dataset);          void save();          void cancelSave();          void requestShowFillUi(AutofillId id, int width, int height, @@ -176,6 +178,7 @@ public final class AutoFillUI {                      hideFillUiUiThread(callback);                      if (mCallback != null) {                          mCallback.authenticate(response.getRequestId(), +                                AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,                                  response.getAuthentication(), response.getClientState());                      }                  } @@ -185,7 +188,8 @@ public final class AutoFillUI {                      log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);                      hideFillUiUiThread(callback);                      if (mCallback != null) { -                        mCallback.fill(response.getRequestId(), dataset); +                        final int datasetIndex = response.getDatasets().indexOf(dataset); +                        mCallback.fill(response.getRequestId(), datasetIndex, dataset);                      }                  } diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 31b4b55818ac..d315b3d7b70f 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -28,6 +28,7 @@ import android.graphics.Point;  import android.graphics.Rect;  import android.service.autofill.Dataset;  import android.service.autofill.FillResponse; +import android.text.TextUtils;  import android.util.Slog;  import android.util.TypedValue;  import android.view.LayoutInflater; @@ -36,10 +37,13 @@ import android.view.View;  import android.view.View.MeasureSpec;  import android.view.ViewGroup;  import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager;  import android.view.autofill.AutofillId;  import android.view.autofill.AutofillValue;  import android.view.autofill.IAutofillWindowPresenter; -import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; +import android.widget.Filter; +import android.widget.Filterable;  import android.widget.ListView;  import android.widget.RemoteViews; @@ -49,6 +53,8 @@ import libcore.util.Objects;  import java.io.PrintWriter;  import java.util.ArrayList; +import java.util.Collections; +import java.util.List;  final class FillUi {      private static final String TAG = "FillUi"; @@ -81,10 +87,12 @@ final class FillUi {      private final @NonNull ListView mListView; -    private final @Nullable ArrayAdapter<ViewItem> mAdapter; +    private final @Nullable ItemsAdapter mAdapter;      private @Nullable String mFilterText; +    private @Nullable AnnounceFilterResult mAnnounceFilterResult; +      private int mContentWidth;      private int mContentHeight; @@ -157,7 +165,9 @@ final class FillUi {                      }                      final AutofillValue value = dataset.getFieldValues().get(index);                      String valueText = null; -                    if (value.isText()) { +                    // If the dataset needs auth - don't add its text to allow guessing +                    // its content based on how filtering behaves. +                    if (value != null && value.isText() && dataset.getAuthentication() == null) {                          valueText = value.getTextValue().toString().toLowerCase();                      } @@ -165,12 +175,7 @@ final class FillUi {                  }              } -            mAdapter = new ArrayAdapter<ViewItem>(context, 0, items) { -                @Override -                public View getView(int position, View convertView, ViewGroup parent) { -                    return getItem(position).getView(); -                } -            }; +            mAdapter = new ItemsAdapter(items);              mListView = decor.findViewById(R.id.autofill_dataset_list);              mListView.setAdapter(mAdapter); @@ -270,8 +275,7 @@ final class FillUi {                  MeasureSpec.AT_MOST);          final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,                  MeasureSpec.AT_MOST); - -        final int itemCount = Math.min(mAdapter.getCount(), VISIBLE_OPTIONS_MAX_COUNT); +        final int itemCount = mAdapter.getCount();          for (int i = 0; i < itemCount; i++) {              View view = mAdapter.getItem(i).getView();              view.measure(widthMeasureSpec, heightMeasureSpec); @@ -281,11 +285,14 @@ final class FillUi {                  mContentWidth = newContentWidth;                  changed = true;              } -            final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y); -            final int newContentHeight = mContentHeight + clampedMeasuredHeight; -            if (newContentHeight != mContentHeight) { -                mContentHeight = newContentHeight; -                changed = true; +            // Update the width to fit only the first items up to max count +            if (i < VISIBLE_OPTIONS_MAX_COUNT) { +                final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y); +                final int newContentHeight = mContentHeight + clampedMeasuredHeight; +                if (newContentHeight != mContentHeight) { +                    mContentHeight = newContentHeight; +                    changed = true; +                }              }          }          return changed; @@ -327,6 +334,10 @@ final class FillUi {              return mDataset;          } +        public String getValue() { +            return mValue; +        } +          @Override          public String toString() {              // Used for filtering in the adapter @@ -435,4 +446,116 @@ final class FillUi {              }          }      } + +    private void announceSearchResultIfNeeded() { +        if (AccessibilityManager.getInstance(mContext).isEnabled()) { +            if (mAnnounceFilterResult == null) { +                mAnnounceFilterResult = new AnnounceFilterResult(); +            } +            mAnnounceFilterResult.post(); +        } +    } + +    private final class ItemsAdapter extends BaseAdapter implements Filterable { +        private @NonNull final List<ViewItem> mAllItems; + +        private @NonNull final List<ViewItem> mFilteredItems = new ArrayList<>(); + +        ItemsAdapter(@NonNull List<ViewItem> items) { +            mAllItems = Collections.unmodifiableList(new ArrayList<>(items)); +            mFilteredItems.addAll(items); +        } + +        @Override +        public Filter getFilter() { +            return new Filter() { +                @Override +                protected FilterResults performFiltering(CharSequence constraint) { +                    // No locking needed as mAllItems is final an immutable +                    final FilterResults results = new FilterResults(); +                    if (TextUtils.isEmpty(constraint)) { +                        results.values = mAllItems; +                        results.count = mAllItems.size(); +                        return results; +                    } +                    final List<ViewItem> filteredItems = new ArrayList<>(); +                    final String constraintLowerCase = constraint.toString().toLowerCase(); +                    final int itemCount = mAllItems.size(); +                    for (int i = 0; i < itemCount; i++) { +                        final ViewItem item = mAllItems.get(i); +                        final String value = item.getValue(); +                        // No value, i.e. null, matches any filter +                        if (value == null +                                || value.toLowerCase().contains(constraintLowerCase)) { +                            filteredItems.add(item); +                        } +                    } +                    results.values = filteredItems; +                    results.count = filteredItems.size(); +                    return results; +                } + +                @Override +                protected void publishResults(CharSequence constraint, FilterResults results) { +                    final boolean resultCountChanged; +                    final int oldItemCount = mFilteredItems.size(); +                    mFilteredItems.clear(); +                    @SuppressWarnings("unchecked") +                    final List<ViewItem> items = (List<ViewItem>) results.values; +                    mFilteredItems.addAll(items); +                    resultCountChanged = (oldItemCount != mFilteredItems.size()); +                    if (resultCountChanged) { +                        announceSearchResultIfNeeded(); +                    } +                    notifyDataSetChanged(); +                } +            }; +        } + +        @Override +        public int getCount() { +            return mFilteredItems.size(); +        } + +        @Override +        public ViewItem getItem(int position) { +            return mFilteredItems.get(position); +        } + +        @Override +        public long getItemId(int position) { +            return position; +        } + +        @Override +        public View getView(int position, View convertView, ViewGroup parent) { +            return getItem(position).getView(); +        } +    } + +    private final class AnnounceFilterResult implements Runnable { +        private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec + +        public void post() { +            remove(); +            mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY); +        } + +        public void remove() { +            mListView.removeCallbacks(this); +        } + +        @Override +        public void run() { +            final int count = mListView.getAdapter().getCount(); +            final String text; +            if (count <= 0) { +                text = mContext.getString(R.string.autofill_picker_no_suggestions); +            } else { +                text = mContext.getResources().getQuantityString( +                        R.plurals.autofill_picker_some_suggestions, count, count); +            } +            mListView.announceForAccessibility(text); +        } +    }  } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 550774a1df58..a378d026d528 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -115,7 +115,8 @@ class AlarmManagerService extends SystemService {      private static final Intent NEXT_ALARM_CLOCK_CHANGED_INTENT =              new Intent(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED) -                    .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); +                    .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING +                            | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);      final LocalLog mLog = new LocalLog(TAG); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 03da5b21fef8..2bd55e2a892a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -158,8 +158,8 @@ public class AccountManagerService          }          @Override -        public void onCleanupUser(int userHandle) { -            mService.onCleanupUser(userHandle); +        public void onStopUser(int userHandle) { +            mService.onStopUser(userHandle);          }      } @@ -1360,8 +1360,8 @@ public class AccountManagerService      } -    private void onCleanupUser(int userId) { -        Log.i(TAG, "onCleanupUser " + userId); +    private void onStopUser(int userId) { +        Log.i(TAG, "onStopUser " + userId);          UserAccounts accounts;          synchronized (mUsers) {              accounts = mUsers.get(userId); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index d5b54ca8bf39..dbe1cb631d86 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1491,6 +1491,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D              // This is the first time we failed -- restart process and              // retry. +            r.launchFailed = true;              app.activities.remove(r);              throw e;          } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 9c5930b71d87..81a14582dae1 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -62,6 +62,7 @@ import android.telephony.CarrierConfigManager;  import android.telephony.TelephonyManager;  import android.text.TextUtils;  import android.util.ArrayMap; +import android.util.LocalLog;  import android.util.Log;  import android.util.SparseArray; @@ -146,6 +147,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering          }      } +    private final static int MAX_LOG_RECORDS = 500; + +    private final LocalLog mLocalLog = new LocalLog(MAX_LOG_RECORDS); +      // used to synchronize public access to members      private final Object mPublicSync;      private final Context mContext; @@ -175,6 +180,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering      public Tethering(Context context, INetworkManagementService nmService,              INetworkStatsService statsService, INetworkPolicyManager policyManager,              Looper looper, MockableSystemProperties systemProperties) { +        mLocalLog.log("CONSTRUCTED");          mContext = context;          mNMService = nmService;          mStatsService = statsService; @@ -952,7 +958,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering          return ConnectivityManager.TETHER_ERROR_NO_ERROR;      } -    // TODO review API - maybe return ArrayList<String> here and below? +    // TODO review API - figure out how to delete these entirely.      public String[] getTetheredIfaces() {          ArrayList<String> list = new ArrayList<String>();          synchronized (mPublicSync) { @@ -1072,19 +1078,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering              //Add states              mInitialState = new InitialState(); -            addState(mInitialState);              mTetherModeAliveState = new TetherModeAliveState(); -            addState(mTetherModeAliveState); -              mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState(); -            addState(mSetIpForwardingEnabledErrorState);              mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState(); -            addState(mSetIpForwardingDisabledErrorState);              mStartTetheringErrorState = new StartTetheringErrorState(); -            addState(mStartTetheringErrorState);              mStopTetheringErrorState = new StopTetheringErrorState(); -            addState(mStopTetheringErrorState);              mSetDnsForwardersErrorState = new SetDnsForwardersErrorState(); + +            addState(mInitialState); +            addState(mTetherModeAliveState); +            addState(mSetIpForwardingEnabledErrorState); +            addState(mSetIpForwardingDisabledErrorState); +            addState(mStartTetheringErrorState); +            addState(mStopTetheringErrorState);              addState(mSetDnsForwardersErrorState);              mNotifyList = new ArrayList<>(); @@ -1092,6 +1098,29 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering              setInitialState(mInitialState);          } +        class InitialState extends State { +            @Override +            public boolean processMessage(Message message) { +                maybeLogMessage(this, message.what); +                switch (message.what) { +                    case EVENT_IFACE_SERVING_STATE_ACTIVE: +                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; +                        if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); +                        handleInterfaceServingStateActive(message.arg1, who); +                        transitionTo(mTetherModeAliveState); +                        break; +                    case EVENT_IFACE_SERVING_STATE_INACTIVE: +                        who = (TetherInterfaceStateMachine)message.obj; +                        if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); +                        handleInterfaceServingStateInactive(who); +                        break; +                    default: +                        return NOT_HANDLED; +                } +                return HANDLED; +            } +        } +          class TetherMasterUtilState extends State {              @Override              public boolean processMessage(Message m) { @@ -1112,6 +1141,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering                  try {                      mNMService.setIpForwardingEnabled(true);                  } catch (Exception e) { +                    mLocalLog.log("ERROR " + e);                      transitionTo(mSetIpForwardingEnabledErrorState);                      return false;                  } @@ -1124,10 +1154,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering                          mNMService.stopTethering();                          mNMService.startTethering(cfg.dhcpRanges);                      } catch (Exception ee) { +                        mLocalLog.log("ERROR " + ee);                          transitionTo(mStartTetheringErrorState);                          return false;                      }                  } +                mLocalLog.log("SET master tether settings: ON");                  return true;              } @@ -1135,16 +1167,19 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering                  try {                      mNMService.stopTethering();                  } catch (Exception e) { +                    mLocalLog.log("ERROR " + e);                      transitionTo(mStopTetheringErrorState);                      return false;                  }                  try {                      mNMService.setIpForwardingEnabled(false);                  } catch (Exception e) { +                    mLocalLog.log("ERROR " + e);                      transitionTo(mSetIpForwardingDisabledErrorState);                      return false;                  }                  transitionTo(mInitialState); +                mLocalLog.log("SET master tether settings: OFF");                  return true;              } @@ -1268,16 +1303,15 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering                      // TODO: remove this invocation of NetworkUtils.makeStrings().                      dnsServers = NetworkUtils.makeStrings(dnses);                  } -                if (VDBG) { -                    Log.d(TAG, "Setting DNS forwarders: Network=" + network + -                           ", dnsServers=" + Arrays.toString(dnsServers)); -                }                  try {                      mNMService.setDnsForwarders(network, dnsServers); +                    mLocalLog.log(String.format( +                            "SET DNS forwarders: network=%s dnsServers=[%s]", +                            network, Arrays.toString(dnsServers)));                  } catch (Exception e) {                      // TODO: Investigate how this can fail and what exactly                      // happens if/when such failures occur. -                    Log.e(TAG, "Setting DNS forwarders failed!"); +                    mLocalLog.log("ERROR setting DNS forwarders failed, " + e);                      transitionTo(mSetDnsForwardersErrorState);                  }              } @@ -1463,31 +1497,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering              }          } -        class InitialState extends State { -            @Override -            public boolean processMessage(Message message) { -                maybeLogMessage(this, message.what); -                boolean retValue = true; -                switch (message.what) { -                    case EVENT_IFACE_SERVING_STATE_ACTIVE: -                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; -                        if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); -                        handleInterfaceServingStateActive(message.arg1, who); -                        transitionTo(mTetherModeAliveState); -                        break; -                    case EVENT_IFACE_SERVING_STATE_INACTIVE: -                        who = (TetherInterfaceStateMachine)message.obj; -                        if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); -                        handleInterfaceServingStateInactive(who); -                        break; -                    default: -                        retValue = false; -                        break; -                } -                return retValue; -            } -        } -          class TetherModeAliveState extends TetherMasterUtilState {              final SimChangeListener simChange = new SimChangeListener(mContext);              boolean mUpstreamWanted = false; @@ -1495,8 +1504,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering              @Override              public void enter() { -                // TODO: examine if we should check the return value. -                turnOnMasterTetherSettings(); // may transition us out +                // If turning on master tether settings fails, we have already +                // transitioned to an error state; exit early. +                if (!turnOnMasterTetherSettings()) { +                    return; +                } +                  simChange.startListening();                  mUpstreamNetworkMonitor.start();                  mOffloadController.start(); @@ -1549,14 +1562,16 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering                          handleInterfaceServingStateInactive(who);                          if (mNotifyList.isEmpty()) { -                            turnOffMasterTetherSettings(); // transitions appropriately -                        } else { -                            if (DBG) { -                                Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + -                                        " live requests:"); -                                for (TetherInterfaceStateMachine o : mNotifyList) { -                                    Log.d(TAG, "  " + o); -                                } +                            // transitions appropriately +                            turnOffMasterTetherSettings(); +                            break; +                        } + +                        if (DBG) { +                            Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + +                                    " live requests:"); +                            for (TetherInterfaceStateMachine o : mNotifyList) { +                                Log.d(TAG, "  " + o);                              }                          }                          // If there has been a change and an upstream is no @@ -1770,6 +1785,12 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering              pw.println("Upstream wanted: " + upstreamWanted());              pw.decreaseIndent();          } + +        pw.println("Log:"); +        pw.increaseIndent(); +        mLocalLog.readOnlyLocalLog().dump(fd, pw, args); +        pw.decreaseIndent(); +          pw.decreaseIndent();      } @@ -1786,10 +1807,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering              }          } -        if (DBG) { -            Log.d(TAG, "iface " + iface + " notified that it was in state " + state + -                    " with error " + error); -        } +        mLocalLog.log(String.format("OBSERVED iface=%s state=%s error=%s", +                iface, state, error));          try {              // Notify that we're tethering (or not) this interface. diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java index dae74db85b2e..16b73d5515e5 100644 --- a/services/usage/java/com/android/server/usage/StorageStatsService.java +++ b/services/usage/java/com/android/server/usage/StorageStatsService.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException;  import android.content.pm.PackageStats;  import android.content.pm.UserInfo;  import android.net.TrafficStats; +import android.net.Uri;  import android.os.Binder;  import android.os.Environment;  import android.os.FileUtils; @@ -467,6 +468,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {                      if (bytesDelta > mMinimumThresholdBytes) {                          mPreviousBytes = mStats.getAvailableBytes();                          recalculateQuotas(getInitializedStrategy()); +                        notifySignificantDelta();                      }                      sendEmptyMessageDelayed(MSG_CHECK_STORAGE_DELTA, DELAY_IN_MILLIS);                      break; @@ -518,4 +520,13 @@ public class StorageStatsService extends IStorageStatsManager.Stub {          return Settings.Global.getInt(                  resolver, Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION, 1) != 0;      } + +    /** +     * Hacky way of notifying that disk space has changed significantly; we do +     * this to cause "available space" values to be requeried. +     */ +    void notifySignificantDelta() { +        mContext.getContentResolver().notifyChange( +                Uri.parse("content://com.android.externalstorage.documents/"), null, false); +    }  } diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index 93cfd11b3d46..5399bb9e5d80 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -71,6 +71,7 @@ import java.util.HashMap;  import java.util.Iterator;  import java.util.List;  import java.util.Map; +import java.util.Objects;  class UsbProfileGroupSettingsManager {      private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName(); @@ -139,7 +140,7 @@ class UsbProfileGroupSettingsManager {              } else {                  UserPackage other = (UserPackage)obj; -                return user.equals(user) && packageName.equals(other.packageName); +                return user.equals(other.user) && packageName.equals(other.packageName);              }          } @@ -319,21 +320,32 @@ class UsbProfileGroupSettingsManager {              return false;          } -        public boolean matches(DeviceFilter f) { -            if (mVendorId != -1 && f.mVendorId != mVendorId) return false; -            if (mProductId != -1 && f.mProductId != mProductId) return false; -            if (f.mManufacturerName != null && mManufacturerName == null) return false; -            if (f.mProductName != null && mProductName == null) return false; -            if (f.mSerialNumber != null && mSerialNumber == null) return false; -            if (mManufacturerName != null && f.mManufacturerName != null && -                !mManufacturerName.equals(f.mManufacturerName)) return false; -            if (mProductName != null && f.mProductName != null && -                !mProductName.equals(f.mProductName)) return false; -            if (mSerialNumber != null && f.mSerialNumber != null && -                !mSerialNumber.equals(f.mSerialNumber)) return false; +        /** +         * If the device described by {@code device} covered by this filter? +         * +         * @param device The device +         * +         * @return {@code true} iff this filter covers the {@code device} +         */ +        public boolean contains(DeviceFilter device) { +            // -1 and null means "match anything" + +            if (mVendorId != -1 && device.mVendorId != mVendorId) return false; +            if (mProductId != -1 && device.mProductId != mProductId) return false; +            if (mManufacturerName != null && !Objects.equals(mManufacturerName, +                    device.mManufacturerName)) { +                return false; +            } +            if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) { +                return false; +            } +            if (mSerialNumber != null +                    && !Objects.equals(mSerialNumber, device.mSerialNumber)) { +                return false; +            }              // check device class/subclass/protocol -            return matches(f.mClass, f.mSubclass, f.mProtocol); +            return matches(device.mClass, device.mSubclass, device.mProtocol);          }          @Override @@ -493,10 +505,19 @@ class UsbProfileGroupSettingsManager {              return true;          } -        public boolean matches(AccessoryFilter f) { -            if (mManufacturer != null && !f.mManufacturer.equals(mManufacturer)) return false; -            if (mModel != null && !f.mModel.equals(mModel)) return false; -            if (mVersion != null && !f.mVersion.equals(mVersion)) return false; +        /** +         * Is the accessories described {@code accessory} covered by this filter? +         * +         * @param accessory A filter describing the accessory +         * +         * @return {@code true} iff this the filter covers the accessory +         */ +        public boolean contains(AccessoryFilter accessory) { +            if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) { +                return false; +            } +            if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false; +            if (mVersion != null && !Objects.equals(accessory.mVersion, mVersion)) return false;              return true;          } @@ -539,16 +560,21 @@ class UsbProfileGroupSettingsManager {      private class MyPackageMonitor extends PackageMonitor {          @Override          public void onPackageAdded(String packageName, int uid) { -            handlePackageUpdate(packageName); -        } +            if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(), +                    UserHandle.getUserId(uid))) { +                return; +            } -        @Override -        public void onPackageUpdateFinished(String packageName, int uid) { -            handlePackageUpdate(packageName); +            handlePackageAdded(new UserPackage(packageName, UserHandle.getUserHandleForUid(uid)));          }          @Override          public void onPackageRemoved(String packageName, int uid) { +            if (!mUserManager.isSameProfileGroup(mParentUser.getIdentifier(), +                    UserHandle.getUserId(uid))) { +                return; +            } +              clearDefaults(packageName, UserHandle.getUserHandleForUid(uid));          }      } @@ -595,7 +621,7 @@ class UsbProfileGroupSettingsManager {              readSettingsLocked();          } -        mPackageMonitor.register(context, null, true); +        mPackageMonitor.register(context, null, UserHandle.ALL, true);          mMtpNotificationManager = new MtpNotificationManager(                  parentUserContext,                  new MtpNotificationManager.OnOpenInAppListener() { @@ -989,9 +1015,12 @@ class UsbProfileGroupSettingsManager {          ApplicationInfo appInfo;          try { -            appInfo = mPackageManager.getApplicationInfo(component.getPackageName(), 0); +            // Fixed handlers are always for parent user +            appInfo = mPackageManager.getApplicationInfoAsUser(component.getPackageName(), 0, +                    mParentUser.getIdentifier());          } catch (NameNotFoundException e) { -            Slog.e(TAG, "Default USB handling package not found: " + component.getPackageName()); +            Slog.e(TAG, "Default USB handling package (" + component.getPackageName() +                    + ") not found  for user " + mParentUser);              return;          } @@ -1175,10 +1204,10 @@ class UsbProfileGroupSettingsManager {          if (userPackage != null) {              // look for default activity              for (final ResolveInfo info : matches) { -                if (info.activityInfo != null -                        && userPackage.packageName.equals(info.activityInfo.packageName) -                        && userPackage.user.getIdentifier() -                                == UserHandle.getUserId(info.activityInfo.applicationInfo.uid)) { +                if (info.activityInfo != null && userPackage.equals( +                        new UserPackage(info.activityInfo.packageName, +                                UserHandle.getUserHandleForUid( +                                        info.activityInfo.applicationInfo.uid)))) {                      return info.activityInfo;                  }              } @@ -1202,35 +1231,53 @@ class UsbProfileGroupSettingsManager {          return null;      } -    private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) { -        boolean changed = false; -        for (DeviceFilter test : mDevicePreferenceMap.keySet()) { -            if (filter.matches(test)) { -                UserPackage currentMatch = mDevicePreferenceMap.get(test); -                if (!currentMatch.packageName.equals(packageName)) { -                    mDevicePreferenceMap.remove(test); -                    changed = true; +    private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage, +            @NonNull DeviceFilter filter) { +        ArrayList<DeviceFilter> keysToRemove = new ArrayList<>(); + +        // The keys in mDevicePreferenceMap are filters that match devices very narrowly +        for (DeviceFilter device : mDevicePreferenceMap.keySet()) { +            if (filter.contains(device)) { +                UserPackage currentMatch = mDevicePreferenceMap.get(device); +                if (!currentMatch.equals(userPackage)) { +                    keysToRemove.add(device);                  }              }          } -        return changed; + +        if (!keysToRemove.isEmpty()) { +            for (DeviceFilter keyToRemove : keysToRemove) { +                mDevicePreferenceMap.remove(keyToRemove); +            } +        } + +        return !keysToRemove.isEmpty();      } -    private boolean clearCompatibleMatchesLocked(String packageName, AccessoryFilter filter) { -        boolean changed = false; -        for (AccessoryFilter test : mAccessoryPreferenceMap.keySet()) { -            if (filter.matches(test)) { -                UserPackage currentMatch = mAccessoryPreferenceMap.get(test); -                if (!currentMatch.packageName.equals(packageName)) { -                    mAccessoryPreferenceMap.remove(test); -                    changed = true; +    private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage, +            @NonNull AccessoryFilter filter) { +        ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>(); + +        // The keys in mAccessoryPreferenceMap are filters that match accessories very narrowly +        for (AccessoryFilter accessory : mAccessoryPreferenceMap.keySet()) { +            if (filter.contains(accessory)) { +                UserPackage currentMatch = mAccessoryPreferenceMap.get(accessory); +                if (!currentMatch.equals(userPackage)) { +                    keysToRemove.add(accessory);                  }              }          } -        return changed; + +        if (!keysToRemove.isEmpty()) { +            for (AccessoryFilter keyToRemove : keysToRemove) { +                mAccessoryPreferenceMap.remove(keyToRemove); +            } +        } + +        return !keysToRemove.isEmpty();      } -    private boolean handlePackageUpdateLocked(String packageName, ActivityInfo aInfo, +    private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,              String metaDataName) {          XmlResourceParser parser = null;          boolean changed = false; @@ -1244,13 +1291,13 @@ class UsbProfileGroupSettingsManager {                  String tagName = parser.getName();                  if ("usb-device".equals(tagName)) {                      DeviceFilter filter = DeviceFilter.read(parser); -                    if (clearCompatibleMatchesLocked(packageName, filter)) { +                    if (clearCompatibleMatchesLocked(userPackage, filter)) {                          changed = true;                      }                  }                  else if ("usb-accessory".equals(tagName)) {                      AccessoryFilter filter = AccessoryFilter.read(parser); -                    if (clearCompatibleMatchesLocked(packageName, filter)) { +                    if (clearCompatibleMatchesLocked(userPackage, filter)) {                          changed = true;                      }                  } @@ -1265,17 +1312,18 @@ class UsbProfileGroupSettingsManager {      }      // Check to see if the package supports any USB devices or accessories. -    // If so, clear any non-matching preferences for matching devices/accessories. -    private void handlePackageUpdate(String packageName) { +    // If so, clear any preferences for matching devices/accessories. +    private void handlePackageAdded(@NonNull UserPackage userPackage) {          synchronized (mLock) {              PackageInfo info;              boolean changed = false;              try { -                info = mPackageManager.getPackageInfo(packageName, -                        PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); +                info = mPackageManager.getPackageInfoAsUser(userPackage.packageName, +                        PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA, +                        userPackage.user.getIdentifier());              } catch (NameNotFoundException e) { -                Slog.e(TAG, "handlePackageUpdate could not find package " + packageName, e); +                Slog.e(TAG, "handlePackageUpdate could not find package " + userPackage, e);                  return;              } @@ -1283,11 +1331,12 @@ class UsbProfileGroupSettingsManager {              if (activities == null) return;              for (int i = 0; i < activities.length; i++) {                  // check for meta-data, both for devices and accessories -                if (handlePackageUpdateLocked(packageName, activities[i], +                if (handlePackageAddedLocked(userPackage, activities[i],                          UsbManager.ACTION_USB_DEVICE_ATTACHED)) {                      changed = true;                  } -                if (handlePackageUpdateLocked(packageName, activities[i], + +                if (handlePackageAddedLocked(userPackage, activities[i],                          UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {                      changed = true;                  } @@ -1318,16 +1367,18 @@ class UsbProfileGroupSettingsManager {       * @param user The user the package belongs to       */      void setDevicePackage(@NonNull UsbDevice device, @Nullable String packageName, -            @Nullable UserHandle user) { +            @NonNull UserHandle user) {          DeviceFilter filter = new DeviceFilter(device);          boolean changed = false;          synchronized (mLock) {              if (packageName == null) {                  changed = (mDevicePreferenceMap.remove(filter) != null);              } else { -                changed = !packageName.equals(mDevicePreferenceMap.get(filter)); +                UserPackage userPackage = new UserPackage(packageName, user); + +                changed = !userPackage.equals(mDevicePreferenceMap.get(filter));                  if (changed) { -                    mDevicePreferenceMap.put(filter, new UserPackage(packageName, user)); +                    mDevicePreferenceMap.put(filter, userPackage);                  }              }              if (changed) { @@ -1344,16 +1395,18 @@ class UsbProfileGroupSettingsManager {       * @param user The user the package belongs to       */      void setAccessoryPackage(@NonNull UsbAccessory accessory, @Nullable String packageName, -            @Nullable UserHandle user) { +            @NonNull UserHandle user) {          AccessoryFilter filter = new AccessoryFilter(accessory);          boolean changed = false;          synchronized (mLock) {              if (packageName == null) {                  changed = (mAccessoryPreferenceMap.remove(filter) != null);              } else { -                changed = !packageName.equals(mAccessoryPreferenceMap.get(filter)); +                UserPackage userPackage = new UserPackage(packageName, user); + +                changed = !userPackage.equals(mAccessoryPreferenceMap.get(filter));                  if (changed) { -                    mAccessoryPreferenceMap.put(filter, new UserPackage(packageName, user)); +                    mAccessoryPreferenceMap.put(filter, userPackage);                  }              }              if (changed) { diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index 7a1c2395c438..3172c6ec856a 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -18,12 +18,13 @@ package com.android.server.connectivity;  import static org.junit.Assert.assertEquals;  import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any;  import static org.mockito.Matchers.anyBoolean;  import static org.mockito.Matchers.anyInt;  import static org.mockito.Matchers.anyString;  import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any;  import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doThrow;  import static org.mockito.Mockito.times;  import static org.mockito.Mockito.verify;  import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -47,6 +48,7 @@ import android.net.wifi.WifiManager;  import android.os.Handler;  import android.os.INetworkManagementService;  import android.os.PersistableBundle; +import android.os.RemoteException;  import android.os.test.TestLooper;  import android.os.UserHandle;  import android.support.test.filters.SmallTest; @@ -352,6 +354,56 @@ public class TetheringTest {                  mTethering.getLastTetherError(mTestIfname));      } -    // TODO: Test that a request for hotspot mode doesn't interface with an +    @Test +    public void failureEnablingIpForwarding() throws Exception { +        when(mConnectivityManager.isTetheringSupported()).thenReturn(true); +        when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); +        doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true); + +        // Emulate pressing the WiFi tethering button. +        mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false); +        mLooper.dispatchAll(); +        verify(mWifiManager, times(1)).startSoftAp(null); +        verifyNoMoreInteractions(mWifiManager); +        verifyNoMoreInteractions(mConnectivityManager); +        verifyNoMoreInteractions(mNMService); + +        // Emulate externally-visible WifiManager effects, causing the +        // per-interface state machine to start up, and telling us that +        // tethering mode is to be started. +        mTethering.interfaceStatusChanged(mTestIfname, true); +        sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_ENABLED); +        mLooper.dispatchAll(); + +        // Activity caused by test_wlan0 becoming available. +        verify(mNMService, times(1)).listInterfaces(); +        // We verify get/set called twice here: once for setup and once during +        // teardown because all events happen over the course of the single +        // dispatchAll() above. +        verify(mNMService, times(2)).getInterfaceConfig(mTestIfname); +        verify(mNMService, times(2)) +                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); +        verify(mNMService, times(1)).tetherInterface(mTestIfname); +        verify(mWifiManager).updateInterfaceIpState( +                mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); +        verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); +        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); +        // This is called, but will throw. +        verify(mNMService, times(1)).setIpForwardingEnabled(true); +        // This never gets called because of the exception thrown above. +        verify(mNMService, times(0)).startTethering(any(String[].class)); +        // When the master state machine transitions to an error state it tells +        // downstream interfaces, which causes us to tell Wi-Fi about the error +        // so it can take down AP mode. +        verify(mNMService, times(1)).untetherInterface(mTestIfname); +        verify(mWifiManager).updateInterfaceIpState( +                mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); + +        verifyNoMoreInteractions(mWifiManager); +        verifyNoMoreInteractions(mConnectivityManager); +        verifyNoMoreInteractions(mNMService); +    } + +    // TODO: Test that a request for hotspot mode doesn't interfere with an      // already operating tethering mode interface.  }  |