diff options
111 files changed, 3870 insertions, 569 deletions
diff --git a/Android.bp b/Android.bp index e2b432cdd8cd..ee593aac39ad 100644 --- a/Android.bp +++ b/Android.bp @@ -689,6 +689,7 @@ java_defaults { "android.hardware.vibrator-V1.2-java", "android.hardware.wifi-V1.0-java-constants", "android.hardware.radio-V1.0-java", + "android.hardware.radio-V1.3-java", "android.hardware.usb.gadget-V1.0-java", ], diff --git a/api/current.txt b/api/current.txt index b5c23f1eb7bc..7e3629274ba6 100755 --- a/api/current.txt +++ b/api/current.txt @@ -21,6 +21,7 @@ package android { field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final java.lang.String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE"; + field public static final java.lang.String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE"; field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; @@ -42266,7 +42267,7 @@ package android.telephony { method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback); } - public class NeighboringCellInfo implements android.os.Parcelable { + public deprecated class NeighboringCellInfo implements android.os.Parcelable { ctor public deprecated NeighboringCellInfo(); ctor public deprecated NeighboringCellInfo(int, int); ctor public NeighboringCellInfo(int, java.lang.String, int); @@ -42723,7 +42724,6 @@ package android.telephony { method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public java.lang.String getNai(); - method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); method public java.lang.String getNetworkOperator(); method public java.lang.String getNetworkOperatorName(); @@ -43025,6 +43025,36 @@ package android.telephony.data { } +package android.telephony.emergency { + + public final class EmergencyNumber implements android.os.Parcelable { + method public int describeContents(); + method public java.lang.String getCountryIso(); + method public int getEmergencyNumberSourceBitmask(); + method public java.util.List<java.lang.Integer> getEmergencyNumberSources(); + method public java.util.List<java.lang.Integer> getEmergencyServiceCategories(); + method public int getEmergencyServiceCategoryBitmask(); + method public java.lang.String getNumber(); + method public boolean isFromSources(int); + method public boolean isInEmergencyServiceCategories(int); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR; + field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8 + field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4 + field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1 + field public static final int EMERGENCY_NUMBER_SOURCE_SIM = 2; // 0x2 + field public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = 64; // 0x40 + field public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2; // 0x2 + field public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 4; // 0x4 + field public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 8; // 0x8 + field public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = 32; // 0x20 + field public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 16; // 0x10 + field public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = 1; // 0x1 + field public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0; // 0x0 + } + +} + package android.telephony.euicc { public final class DownloadableSubscription implements android.os.Parcelable { @@ -54874,6 +54904,7 @@ package dalvik.system { public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader { ctor public DelegateLastClassLoader(java.lang.String, java.lang.ClassLoader); ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader); + ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader, boolean); } public class DexClassLoader extends dalvik.system.BaseDexClassLoader { diff --git a/api/removed.txt b/api/removed.txt index f35348ec8bfd..fba5187929fd 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -540,6 +540,7 @@ package android.telephony { } public class TelephonyManager { + method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); } diff --git a/api/system-current.txt b/api/system-current.txt index 231a32785b00..89ac7210de46 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5138,9 +5138,12 @@ package android.telephony { } public class ServiceState implements android.os.Parcelable { + method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int); method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(); - method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int); - method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int); + method public deprecated java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int); + method public deprecated android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int); + method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int); + method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int); } public final class SmsManager { @@ -5657,7 +5660,7 @@ package android.telephony.ims { field public static final java.lang.String EXTRA_CODEC = "Codec"; field public static final java.lang.String EXTRA_DIALSTRING = "dialstring"; field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText"; - field public static final java.lang.String EXTRA_E_CALL = "e_call"; + field public static final java.lang.String EXTRA_EMERGENCY_CALL = "e_call"; field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull"; field public static final java.lang.String EXTRA_OI = "oi"; field public static final java.lang.String EXTRA_OIR = "oir"; diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 4bbe04204a54..72a41036e595 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -425,7 +425,19 @@ WorkerThreadSection::WorkerThreadSection(int id, const int64_t timeoutMs, bool u WorkerThreadSection::~WorkerThreadSection() {} +void sigpipe_handler(int signum) { + if (signum == SIGPIPE) { + ALOGE("Wrote to a broken pipe\n"); + } else { + ALOGE("Received unexpected signal: %d\n", signum); + } +} + static void* worker_thread_func(void* cookie) { + // Don't crash the service if we write to a closed pipe (which can happen if + // dumping times out). + signal(SIGPIPE, sigpipe_handler); + WorkerThreadData* data = (WorkerThreadData*)cookie; status_t err = data->section->BlockingCall(data->pipe.writeFd().get()); @@ -506,6 +518,7 @@ status_t WorkerThreadSection::Execute(ReportRequestSet* requests) const { } } } + write_section_stats(requests->sectionStats(this->id), buffer); if (timedOut || buffer.timedOut()) { ALOGW("WorkerThreadSection '%s' timed out", this->name.string()); @@ -813,7 +826,10 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { } } gLastLogsRetrieved[mLogID] = lastTimestamp; - proto.flush(pipeWriteFd); + if (!proto.flush(pipeWriteFd) && errno == EPIPE) { + ALOGE("[%s] wrote to a broken pipe\n", this->name.string()); + return EPIPE; + } return NO_ERROR; } @@ -915,7 +931,7 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const { break; } if (cStatus != NO_ERROR) { - ALOGE("TombstoneSection '%s' child had an issue: %s\n", this->name.string(), strerror(-cStatus)); + ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus)); } auto dump = std::make_unique<char[]>(buffer.size()); @@ -934,7 +950,13 @@ status_t TombstoneSection::BlockingCall(int pipeWriteFd) const { dumpPipe.readFd().reset(); } - proto.flush(pipeWriteFd); + if (!proto.flush(pipeWriteFd) && errno == EPIPE) { + ALOGE("[%s] wrote to a broken pipe\n", this->name.string()); + if (err != NO_ERROR) { + return EPIPE; + } + } + return err; } diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index a031a15fe7c9..2f89370b0d26 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -173,7 +173,7 @@ private: */ class TombstoneSection : public WorkerThreadSection { public: - TombstoneSection(int id, const char* type, int64_t timeoutMs = 30000 /* 30 seconds */); + TombstoneSection(int id, const char* type, int64_t timeoutMs = 120000 /* 2 minutes */); virtual ~TombstoneSection(); virtual status_t BlockingCall(int pipeWriteFd) const; diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index fff909c12441..581ed3e5ef22 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -489,6 +489,9 @@ const std::map<string, uint32_t> UidMap::sAidToUidMapping = {{"AID_ROOT", 0}, {"AID_RESERVED_DISK", 1065}, {"AID_STATSD", 1066}, {"AID_INCIDENTD", 1067}, + {"AID_SECURE_ELEMENT", 1068}, + {"AID_LMKD", 1069}, + {"AID_LLKD", 1070}, {"AID_SHELL", 2000}, {"AID_CACHE", 2001}, {"AID_DIAG", 2002}}; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index f2e907833612..8333b817add0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -26,7 +26,6 @@ import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -3801,8 +3800,9 @@ public class ConnectivityManager { private void unsupportedStartingFrom(int version) { if (Process.myUid() == Process.SYSTEM_UID) { - // The getApplicationInfo() call we make below is not supported in system context, and - // we want to allow the system to use these APIs anyway. + // The getApplicationInfo() call we make below is not supported in system context. Let + // the call through here, and rely on the fact that ConnectivityService will refuse to + // allow the system to use these APIs anyway. return; } @@ -3819,11 +3819,6 @@ public class ConnectivityManager { // functions by accessing ConnectivityService directly. However, it should be clear that doing // so is unsupported and may break in the future. http://b/22728205 private void checkLegacyRoutingApiAccess() { - if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS") - == PackageManager.PERMISSION_GRANTED) { - return; - } - unsupportedStartingFrom(VERSION_CODES.M); } diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java index 534ef8d8e0e9..4b5a7b40fe44 100644 --- a/core/java/android/preference/DialogPreference.java +++ b/core/java/android/preference/DialogPreference.java @@ -43,7 +43,7 @@ import android.widget.TextView; * A base class for {@link Preference} objects that are * dialog-based. These preferences will, when clicked, open a dialog showing the * actual preference controls. - * + * * @attr ref android.R.styleable#DialogPreference_dialogTitle * @attr ref android.R.styleable#DialogPreference_dialogMessage * @attr ref android.R.styleable#DialogPreference_dialogIcon @@ -56,7 +56,7 @@ public abstract class DialogPreference extends Preference implements PreferenceManager.OnActivityDestroyListener { @UnsupportedAppUsage private AlertDialog.Builder mBuilder; - + @UnsupportedAppUsage private CharSequence mDialogTitle; @UnsupportedAppUsage @@ -77,6 +77,14 @@ public abstract class DialogPreference extends Preference implements @UnsupportedAppUsage private int mWhichButtonClicked; + /** Dismiss the dialog on the UI thread, but not inline with handlers */ + private final Runnable mDismissRunnable = new Runnable() { + @Override + public void run() { + mDialog.dismiss(); + } + }; + public DialogPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); @@ -112,7 +120,7 @@ public abstract class DialogPreference extends Preference implements /** * Sets the title of the dialog. This will be shown on subsequent dialogs. - * + * * @param dialogTitle The title. */ public void setDialogTitle(CharSequence dialogTitle) { @@ -126,7 +134,7 @@ public abstract class DialogPreference extends Preference implements public void setDialogTitle(int dialogTitleResId) { setDialogTitle(getContext().getString(dialogTitleResId)); } - + /** * Returns the title to be shown on subsequent dialogs. * @return The title. @@ -134,7 +142,7 @@ public abstract class DialogPreference extends Preference implements public CharSequence getDialogTitle() { return mDialogTitle; } - + /** * Sets the message of the dialog. This will be shown on subsequent dialogs. * <p> @@ -142,7 +150,7 @@ public abstract class DialogPreference extends Preference implements * list-based dialogs, for example. If setting a custom View on a dialog via * {@link #setDialogLayoutResource(int)}, include a text View with ID * {@link android.R.id#message} and it will be populated with this message. - * + * * @param dialogMessage The message. */ public void setDialogMessage(CharSequence dialogMessage) { @@ -156,7 +164,7 @@ public abstract class DialogPreference extends Preference implements public void setDialogMessage(int dialogMessageResId) { setDialogMessage(getContext().getString(dialogMessageResId)); } - + /** * Returns the message to be shown on subsequent dialogs. * @return The message. @@ -164,26 +172,26 @@ public abstract class DialogPreference extends Preference implements public CharSequence getDialogMessage() { return mDialogMessage; } - + /** * Sets the icon of the dialog. This will be shown on subsequent dialogs. - * + * * @param dialogIcon The icon, as a {@link Drawable}. */ public void setDialogIcon(Drawable dialogIcon) { mDialogIcon = dialogIcon; } - + /** * Sets the icon (resource ID) of the dialog. This will be shown on * subsequent dialogs. - * + * * @param dialogIconRes The icon, as a resource ID. */ public void setDialogIcon(@DrawableRes int dialogIconRes) { mDialogIcon = getContext().getDrawable(dialogIconRes); } - + /** * Returns the icon to be shown on subsequent dialogs. * @return The icon, as a {@link Drawable}. @@ -191,11 +199,11 @@ public abstract class DialogPreference extends Preference implements public Drawable getDialogIcon() { return mDialogIcon; } - + /** * Sets the text of the positive button of the dialog. This will be shown on * subsequent dialogs. - * + * * @param positiveButtonText The text of the positive button. */ public void setPositiveButtonText(CharSequence positiveButtonText) { @@ -209,27 +217,27 @@ public abstract class DialogPreference extends Preference implements public void setPositiveButtonText(@StringRes int positiveButtonTextResId) { setPositiveButtonText(getContext().getString(positiveButtonTextResId)); } - + /** * Returns the text of the positive button to be shown on subsequent * dialogs. - * + * * @return The text of the positive button. */ public CharSequence getPositiveButtonText() { return mPositiveButtonText; } - + /** * Sets the text of the negative button of the dialog. This will be shown on * subsequent dialogs. - * + * * @param negativeButtonText The text of the negative button. */ public void setNegativeButtonText(CharSequence negativeButtonText) { mNegativeButtonText = negativeButtonText; } - + /** * @see #setNegativeButtonText(CharSequence) * @param negativeButtonTextResId The negative button text as a resource. @@ -237,38 +245,38 @@ public abstract class DialogPreference extends Preference implements public void setNegativeButtonText(@StringRes int negativeButtonTextResId) { setNegativeButtonText(getContext().getString(negativeButtonTextResId)); } - + /** * Returns the text of the negative button to be shown on subsequent * dialogs. - * + * * @return The text of the negative button. */ public CharSequence getNegativeButtonText() { return mNegativeButtonText; } - + /** * Sets the layout resource that is inflated as the {@link View} to be shown * as the content View of subsequent dialogs. - * + * * @param dialogLayoutResId The layout resource ID to be inflated. * @see #setDialogMessage(CharSequence) */ public void setDialogLayoutResource(int dialogLayoutResId) { mDialogLayoutResId = dialogLayoutResId; } - + /** * Returns the layout resource that is used as the content View for * subsequent dialogs. - * + * * @return The layout resource. */ public int getDialogLayoutResource() { return mDialogLayoutResId; } - + /** * Prepares the dialog builder to be shown when the preference is clicked. * Use this to set custom properties on the dialog. @@ -278,7 +286,7 @@ public abstract class DialogPreference extends Preference implements */ protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { } - + @Override protected void onClick() { if (mDialog != null && mDialog.isShowing()) return; @@ -290,14 +298,14 @@ public abstract class DialogPreference extends Preference implements * Shows the dialog associated with this Preference. This is normally initiated * automatically on clicking on the preference. Call this method if you need to * show the dialog on some other event. - * + * * @param state Optional instance state to restore on the dialog */ protected void showDialog(Bundle state) { Context context = getContext(); mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE; - + mBuilder = new AlertDialog.Builder(context) .setTitle(mDialogTitle) .setIcon(mDialogIcon) @@ -311,11 +319,11 @@ public abstract class DialogPreference extends Preference implements } else { mBuilder.setMessage(mDialogMessage); } - + onPrepareDialogBuilder(mBuilder); - + getPreferenceManager().registerOnActivityDestroyListener(this); - + // Create the dialog final Dialog dialog = mDialog = mBuilder.create(); if (state != null) { @@ -324,10 +332,29 @@ public abstract class DialogPreference extends Preference implements if (needInputMethod()) { requestInputMethod(dialog); } + dialog.setOnShowListener(new DialogInterface.OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + removeDismissCallbacks(); + } + }); dialog.setOnDismissListener(this); dialog.show(); } + void postDismiss() { + removeDismissCallbacks(); + View decorView = mDialog.getWindow().getDecorView(); + decorView.post(mDismissRunnable); + } + + private void removeDismissCallbacks() { + if (mDialog != null && mDialog.getWindow() != null + && mDialog.getWindow().getDecorView() != null) { + mDialog.getWindow().getDecorView().removeCallbacks(mDismissRunnable); + } + } + /** * Returns whether the preference needs to display a soft input method when the dialog * is displayed. Default is false. Subclasses should override this method if they need @@ -350,7 +377,7 @@ public abstract class DialogPreference extends Preference implements * Creates the content view for the dialog (if a custom content view is * required). By default, it inflates the dialog layout resource if it is * set. - * + * * @return The content View for the dialog. * @see #setLayoutResource(int) */ @@ -358,48 +385,49 @@ public abstract class DialogPreference extends Preference implements if (mDialogLayoutResId == 0) { return null; } - + LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext()); return inflater.inflate(mDialogLayoutResId, null); } - + /** * Binds views in the content View of the dialog to data. * <p> * Make sure to call through to the superclass implementation. - * + * * @param view The content View of the dialog, if it is custom. */ @CallSuper protected void onBindDialogView(View view) { View dialogMessageView = view.findViewById(com.android.internal.R.id.message); - + if (dialogMessageView != null) { final CharSequence message = getDialogMessage(); int newVisibility = View.GONE; - + if (!TextUtils.isEmpty(message)) { if (dialogMessageView instanceof TextView) { ((TextView) dialogMessageView).setText(message); } - + newVisibility = View.VISIBLE; } - + if (dialogMessageView.getVisibility() != newVisibility) { dialogMessageView.setVisibility(newVisibility); } } } - + public void onClick(DialogInterface dialog, int which) { mWhichButtonClicked = which; } - + + @Override public void onDismiss(DialogInterface dialog) { - + removeDismissCallbacks(); getPreferenceManager().unregisterOnActivityDestroyListener(this); - + mDialog = null; onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE); } @@ -407,7 +435,7 @@ public abstract class DialogPreference extends Preference implements /** * Called when the dialog is dismissed and should be used to save data to * the {@link SharedPreferences}. - * + * * @param positiveResult Whether the positive button was clicked (true), or * the negative button was clicked or the dialog was canceled (false). */ @@ -416,7 +444,7 @@ public abstract class DialogPreference extends Preference implements /** * Gets the dialog that is shown by this preference. - * + * * @return The dialog, or null if a dialog is not being shown. */ public Dialog getDialog() { @@ -427,11 +455,11 @@ public abstract class DialogPreference extends Preference implements * {@inheritDoc} */ public void onActivityDestroy() { - + if (mDialog == null || !mDialog.isShowing()) { return; } - + mDialog.dismiss(); } @@ -466,7 +494,7 @@ public abstract class DialogPreference extends Preference implements private static class SavedState extends BaseSavedState { boolean isDialogShowing; Bundle dialogBundle; - + public SavedState(Parcel source) { super(source); isDialogShowing = source.readInt() == 1; @@ -495,5 +523,5 @@ public abstract class DialogPreference extends Preference implements } }; } - + } diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java index e7dec0ec2bba..c0c71af8639e 100644 --- a/core/java/android/preference/ListPreference.java +++ b/core/java/android/preference/ListPreference.java @@ -33,7 +33,7 @@ import android.util.AttributeSet; * <p> * This preference will store a string into the SharedPreferences. This string will be the value * from the {@link #setEntryValues(CharSequence[])} array. - * + * * @attr ref android.R.styleable#ListPreference_entries * @attr ref android.R.styleable#ListPreference_entryValues */ @@ -193,7 +193,7 @@ public class ListPreference extends DialogPreference { /** * Sets the value to the given index from the entry values. - * + * * @param index The index of the value to set. */ public void setValueIndex(int index) { @@ -201,30 +201,30 @@ public class ListPreference extends DialogPreference { setValue(mEntryValues[index].toString()); } } - + /** * Returns the value of the key. This should be one of the entries in * {@link #getEntryValues()}. - * + * * @return The value of the key. */ public String getValue() { - return mValue; + return mValue; } - + /** * Returns the entry corresponding to the current value. - * + * * @return The entry corresponding to the current value, or null. */ public CharSequence getEntry() { int index = getValueIndex(); return index >= 0 && mEntries != null ? mEntries[index] : null; } - + /** * Returns the index of the given value (in the entry values array). - * + * * @param value The value whose index should be returned. * @return The index of the value, or -1 if not found. */ @@ -238,22 +238,22 @@ public class ListPreference extends DialogPreference { } return -1; } - + private int getValueIndex() { return findIndexOfValue(mValue); } - + @Override protected void onPrepareDialogBuilder(Builder builder) { super.onPrepareDialogBuilder(builder); - + if (mEntries == null || mEntryValues == null) { throw new IllegalStateException( "ListPreference requires an entries array and an entryValues array."); } mClickedDialogEntryIndex = getValueIndex(); - builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, + builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mClickedDialogEntryIndex = which; @@ -263,10 +263,10 @@ public class ListPreference extends DialogPreference { * click, and dismisses the dialog. */ ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE); - dialog.dismiss(); + postDismiss(); } }); - + /* * The typical interaction for list-based dialogs is to have * click-on-an-item dismiss the dialog instead of the user having to @@ -278,7 +278,7 @@ public class ListPreference extends DialogPreference { @Override protected void onDialogClosed(boolean positiveResult) { super.onDialogClosed(positiveResult); - + if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) { String value = mEntryValues[mClickedDialogEntryIndex].toString(); if (callChangeListener(value)) { @@ -296,7 +296,7 @@ public class ListPreference extends DialogPreference { protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue); } - + @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); @@ -304,7 +304,7 @@ public class ListPreference extends DialogPreference { // No need to save instance state since it's persistent return superState; } - + final SavedState myState = new SavedState(superState); myState.value = getValue(); return myState; @@ -317,15 +317,15 @@ public class ListPreference extends DialogPreference { super.onRestoreInstanceState(state); return; } - + SavedState myState = (SavedState) state; super.onRestoreInstanceState(myState.getSuperState()); setValue(myState.value); } - + private static class SavedState extends BaseSavedState { String value; - + public SavedState(Parcel source) { super(source); value = source.readString(); @@ -352,5 +352,5 @@ public class ListPreference extends DialogPreference { } }; } - + } diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 149f2b40add5..dd011a0ff0ec 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -1444,18 +1444,6 @@ public final class MediaStore { } public static final class Media implements AudioColumns { - - private static final String[] EXTERNAL_PATHS; - - static { - String secondary_storage = System.getenv("SECONDARY_STORAGE"); - if (secondary_storage != null) { - EXTERNAL_PATHS = secondary_storage.split(":"); - } else { - EXTERNAL_PATHS = new String[0]; - } - } - /** * Get the content:// style URI for the audio media table on the * given volume. @@ -1469,14 +1457,9 @@ public final class MediaStore { } public static Uri getContentUriForPath(String path) { - for (String ep : EXTERNAL_PATHS) { - if (path.startsWith(ep)) { - return EXTERNAL_CONTENT_URI; - } - } - - return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ? - EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI); + return (path.startsWith( + Environment.getStorageDirectory().getAbsolutePath() + "/") + ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4b76ba8be63f..29dbb83ca591 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9131,6 +9131,19 @@ public final class Settings { public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; /** + * Whether the wifi data connection should remain active even when higher + * priority networks like Ethernet are active, to keep both networks. + * In the case where higher priority networks are connected, wifi will be + * unused unless an application explicitly requests to use it. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + * @hide + */ + public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; + + /** * Size of the event buffer for IP connectivity metrics. * @hide */ @@ -9469,8 +9482,7 @@ public final class Settings { * Use the old dnsmasq DHCP server for tethering instead of the framework implementation. * * Integer values are interpreted as boolean, and the absence of an explicit setting - * is interpreted as |true|. - * TODO: make the default |false| + * is interpreted as |false|. * @hide */ public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java index 7fd83bc6c8b9..f6d80a572c75 100644 --- a/core/java/com/android/internal/util/DumpUtils.java +++ b/core/java/com/android/internal/util/DumpUtils.java @@ -34,9 +34,18 @@ import java.util.function.Predicate; /** * Helper functions for dumping the state of system services. * Test: - atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java + atest FrameworksCoreTests:DumpUtilsTest */ public final class DumpUtils { + + /** + * List of component names that should be dumped in the bug report critical section. + * + * @hide + */ + public static final ComponentName[] CRITICAL_SECTION_COMPONENTS = { + new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService") + }; private static final String TAG = "DumpUtils"; private static final boolean DEBUG = false; @@ -213,6 +222,45 @@ public final class DumpUtils { } /** + * Return whether a package should be dumped in the critical section. + */ + private static boolean isCriticalPackage(@Nullable ComponentName cname) { + if (cname == null) { + return false; + } + + for (int i = 0; i < CRITICAL_SECTION_COMPONENTS.length; i++) { + if (cname.equals(CRITICAL_SECTION_COMPONENTS[i])) { + return true; + } + } + return false; + } + + /** + * Return whether a package name is considered to be part of the platform and in the critical + * section. + * + * @hide + */ + public static boolean isPlatformCriticalPackage(@Nullable ComponentName.WithComponentName wcn) { + return (wcn != null) && isPlatformPackage(wcn.getComponentName()) && + isCriticalPackage(wcn.getComponentName()); + } + + /** + * Return whether a package name is considered to be part of the platform but not in the the + * critical section. + * + * @hide + */ + public static boolean isPlatformNonCriticalPackage( + @Nullable ComponentName.WithComponentName wcn) { + return (wcn != null) && isPlatformPackage(wcn.getComponentName()) && + !isCriticalPackage(wcn.getComponentName()); + } + + /** * Used for dumping providers and services. Return a predicate for a given filter string. * @hide */ @@ -238,6 +286,16 @@ public final class DumpUtils { return DumpUtils::isNonPlatformPackage; } + // Dump all platform-critical? + if ("all-platform-critical".equals(filterString)) { + return DumpUtils::isPlatformCriticalPackage; + } + + // Dump all platform-non-critical? + if ("all-platform-non-critical".equals(filterString)) { + return DumpUtils::isPlatformNonCriticalPackage; + } + // Is the filter a component name? If so, do an exact match. final ComponentName filterCname = ComponentName.unflattenFromString(filterString); if (filterCname != null) { diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp index 6df23f72bdd3..a1f2377041e8 100644 --- a/core/jni/android_net_LocalSocketImpl.cpp +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -58,6 +58,11 @@ socket_connect_local(JNIEnv *env, jobject object, int ret; int fd; + if (name == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (env->ExceptionCheck()) { diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp index c15b7ee4fe04..109e65c4a1d0 100644 --- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp +++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp @@ -285,10 +285,6 @@ static int statsLinesToNetworkStats(JNIEnv* env, jclass clazz, jobject stats, static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path, jint limitUid, jobjectArray limitIfacesObj, jint limitTag, jboolean useBpfStats) { - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return -1; - } std::vector<std::string> limitIfaces; if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) { @@ -308,6 +304,11 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstr if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0) return -1; } else { + ScopedUtfChars path8(env, path); + if (path8.c_str() == NULL) { + ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str()); + return -1; + } if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid, path8.c_str()) < 0) return -1; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ac9617c5dc62..73bb1fc1b4a7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1827,6 +1827,13 @@ <permission android:name="android.permission.BIND_SCREENING_SERVICE" android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.telecom.CallRedirectionService}, + to ensure that only the system can bind to it. + <p>Protection level: signature|privileged + --> + <permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE" + android:protectionLevel="signature|privileged" /> + <!-- Must be required by a {@link android.telecom.ConnectionService}, to ensure that only the system can bind to it. @deprecated {@link android.telecom.ConnectionService}s should require diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java index 1349844c80cf..1286b137cc1f 100644 --- a/core/tests/coretests/src/android/net/LocalSocketTest.java +++ b/core/tests/coretests/src/android/net/LocalSocketTest.java @@ -22,6 +22,7 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; + import junit.framework.TestCase; import java.io.FileDescriptor; @@ -39,6 +40,20 @@ public class LocalSocketTest extends TestCase { ls = new LocalSocket(); + try { + ls.connect(new LocalSocketAddress(null)); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + // pass + } + + try { + ls.bind(new LocalSocketAddress(null)); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + // pass + } + ls.connect(new LocalSocketAddress("android.net.LocalSocketTest")); ls1 = ss.accept(); diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index cd9658348212..23be634da05f 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -456,6 +456,7 @@ public class SettingsBackupTest { Settings.Global.WFC_IMS_MODE, Settings.Global.WFC_IMS_ROAMING_ENABLED, Settings.Global.WFC_IMS_ROAMING_MODE, + Settings.Global.WIFI_ALWAYS_REQUESTED, Settings.Global.WIFI_BADGING_THRESHOLDS, Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS, Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, diff --git a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java index 45b19bccff88..a44b86074ee2 100644 --- a/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/DumpUtilsTest.java @@ -15,8 +15,11 @@ */ package com.android.internal.util; +import static com.android.internal.util.DumpUtils.CRITICAL_SECTION_COMPONENTS; import static com.android.internal.util.DumpUtils.filterRecord; import static com.android.internal.util.DumpUtils.isNonPlatformPackage; +import static com.android.internal.util.DumpUtils.isPlatformCriticalPackage; +import static com.android.internal.util.DumpUtils.isPlatformNonCriticalPackage; import static com.android.internal.util.DumpUtils.isPlatformPackage; import android.content.ComponentName; @@ -25,7 +28,7 @@ import junit.framework.TestCase; /** * Run with: - atest /android/pi-dev/frameworks/base/core/tests/coretests/src/com/android/internal/util/DumpTest.java + atest FrameworksCoreTests:DumpUtilsTest */ public class DumpUtilsTest extends TestCase { @@ -89,6 +92,32 @@ public class DumpUtilsTest extends TestCase { assertTrue(isNonPlatformPackage(wcn("com.google.def/abc"))); } + public void testIsPlatformCriticalPackage() { + for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) { + assertTrue(isPlatformCriticalPackage(() -> componentName)); + assertTrue(isPlatformPackage(componentName)); + } + assertFalse(isPlatformCriticalPackage(wcn("com.google.p/abc"))); + assertFalse(isPlatformCriticalPackage(wcn("com.android.def/abc"))); + assertFalse(isPlatformCriticalPackage(wcn("com.android.abc"))); + assertFalse(isPlatformCriticalPackage(wcn("com.android"))); + assertFalse(isPlatformCriticalPackage(wcn(null))); + assertFalse(isPlatformCriticalPackage(null)); + } + + public void testIsPlatformNonCriticalPackage() { + for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) { + assertFalse(isPlatformNonCriticalPackage(() -> componentName)); + } + assertTrue(isPlatformNonCriticalPackage(wcn("android/abc"))); + assertTrue(isPlatformNonCriticalPackage(wcn("android.abc/abc"))); + assertTrue(isPlatformNonCriticalPackage(wcn("com.android.def/abc"))); + + assertFalse(isPlatformNonCriticalPackage(wcn("com.google.def/abc"))); + assertFalse(isPlatformNonCriticalPackage(wcn(null))); + assertFalse(isPlatformNonCriticalPackage(null)); + } + public void testFilterRecord() { assertFalse(filterRecord(null).test(wcn("com.google.p/abc"))); assertFalse(filterRecord(null).test(wcn("com.android.p/abc"))); @@ -105,6 +134,19 @@ public class DumpUtilsTest extends TestCase { assertFalse(filterRecord("all-non-platform").test(wcn("com.android.p/abc"))); assertFalse(filterRecord("all-non-platform").test(wcn(null))); + for (final ComponentName componentName : CRITICAL_SECTION_COMPONENTS) { + assertTrue(filterRecord("all-platform-critical").test((() -> componentName))); + assertFalse(filterRecord("all-platform-non-critical").test((() -> componentName))); + assertTrue(filterRecord("all-platform").test((() -> componentName))); + } + assertFalse(filterRecord("all-platform-critical").test(wcn("com.google.p/abc"))); + assertFalse(filterRecord("all-platform-critical").test(wcn("com.android.p/abc"))); + assertFalse(filterRecord("all-platform-critical").test(wcn(null))); + + assertTrue(filterRecord("all-platform-non-critical").test(wcn("com.android.p/abc"))); + assertFalse(filterRecord("all-platform-non-critical").test(wcn("com.google.p/abc"))); + assertFalse(filterRecord("all-platform-non-critical").test(wcn(null))); + // Partial string match. assertTrue(filterRecord("abc").test(wcn("com.google.p/.abc"))); assertFalse(filterRecord("abc").test(wcn("com.google.p/.def"))); diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index e4cd6a8ab6b8..6c9eee0b8835 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -47,11 +47,11 @@ class unique_cptr { constexpr unique_cptr() : ptr_(nullptr) {} constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {} explicit unique_cptr(pointer ptr) : ptr_(ptr) {} - unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; } + unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; } ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); } - inline unique_cptr& operator=(unique_cptr&& o) { + inline unique_cptr& operator=(unique_cptr&& o) noexcept { if (&o == this) { return *this; } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 02e1f2f530d1..2eed70f023b1 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -159,8 +159,9 @@ public class MediaScanner implements AutoCloseable { public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild"; public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint"; - private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio"; - private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio"; + private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio"; + private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio"; + private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio"; private static String sLastInternalScanFingerprint; private static final String[] ID3_GENRES = { @@ -1192,6 +1193,9 @@ public class MediaScanner implements AutoCloseable { if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR) || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR) || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR) + || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR) + || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR) + || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR) || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR) || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR) || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) { diff --git a/media/jni/OWNERS b/media/jni/OWNERS new file mode 100644 index 000000000000..bb91d4b26ecc --- /dev/null +++ b/media/jni/OWNERS @@ -0,0 +1,2 @@ +# extra for MTP related files +per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS new file mode 100644 index 000000000000..1928ba811e7e --- /dev/null +++ b/media/tests/MtpTests/OWNERS @@ -0,0 +1,7 @@ +set noparent + +marcone@google.com +jsharkey@android.com +jameswei@google.com +rmojumder@google.com + diff --git a/packages/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml new file mode 100644 index 000000000000..c3d32de4bfaf --- /dev/null +++ b/packages/ExtServices/tests/AndroidTest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Tests for ExtServices"> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="ExtServicesUnitTests.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="ExtServicesUnitTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="android.ext.services.tests.unit" /> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index a46c3e64c163..76a216d1be48 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1125,4 +1125,7 @@ <!-- time label for event have that happened very recently [CHAR LIMIT=60] --> <string name="time_unit_just_now">Just now</string> + + <!-- The notice header of Third-party licenses. not translatable --> + <string name="notice_header" translatable="false"></string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 08fbbedac19c..75cbb6589037 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -29,6 +29,7 @@ import android.bluetooth.BluetoothMapClient; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothPbap; import android.bluetooth.BluetoothPbapClient; +import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -101,6 +102,7 @@ public class LocalBluetoothProfileManager { private final boolean mUsePbapPce; private final boolean mUseMapClient; private HearingAidProfile mHearingAidProfile; + private SapProfile mSapProfile; /** * Mapping from profile name, e.g. "HEADSET" to profile object. @@ -196,12 +198,14 @@ public class LocalBluetoothProfileManager { if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) { if (mA2dpSinkProfile == null) { if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile"); - mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this); + mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, + mDeviceManager, this); addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME, BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); } } else if (mA2dpSinkProfile != null) { - Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing."); + Log.w(TAG, "Warning: A2DP Sink profile was previously added but the " + + "UUID is now missing."); } // Headset / Handsfree @@ -217,7 +221,8 @@ public class LocalBluetoothProfileManager { BluetoothHeadset.STATE_AUDIO_DISCONNECTED); } } else if (mHeadsetProfile != null) { - Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing."); + Log.w(TAG, "Warning: HEADSET profile was previously added but the " + + "UUID is now missing."); } // Headset HF @@ -249,7 +254,8 @@ public class LocalBluetoothProfileManager { } } else if (mMapClientProfile != null) { Log.w(TAG, - "Warning: MAP Client profile was previously added but the UUID is now missing."); + "Warning: MAP Client profile was previously added but the " + + "UUID is now missing."); } else { Log.d(TAG, "MAP Client Uuid not found."); } @@ -266,7 +272,7 @@ public class LocalBluetoothProfileManager { Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing."); } - //PBAP Client + // PBAP Client if (mUsePbapPce) { if (mPbapClientProfile == null) { if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile"); @@ -280,18 +286,27 @@ public class LocalBluetoothProfileManager { "Warning: PBAP Client profile was previously added but the UUID is now missing."); } - //Hearing Aid Client + // Hearing Aid Client if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) { if (mHearingAidProfile == null) { if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile"); - mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this); + mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, + mDeviceManager, this); addProfile(mHearingAidProfile, HearingAidProfile.NAME, BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); } } else if (mHearingAidProfile != null) { - Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing."); + Log.w(TAG, "Warning: Hearing Aid profile was previously added but the " + + "UUID is now missing."); } + // SAP + if (mSapProfile == null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) { + Log.d(TAG, "Adding local SAP profile"); + mSapProfile = new SapProfile(mContext, mLocalAdapter, mDeviceManager, this); + addProfile(mSapProfile, SapProfile.NAME, + BluetoothSap.ACTION_CONNECTION_STATE_CHANGED); + } mEventManager.registerProfileIntentReceiver(); // There is no local SDP record for HID and Settings app doesn't control PBAP Server. @@ -635,6 +650,11 @@ public class LocalBluetoothProfileManager { removedProfiles.remove(mHearingAidProfile); } + if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) { + profiles.add(mSapProfile); + removedProfiles.remove(mSapProfile); + } + if (DEBUG) { Log.d(TAG,"New Profiles" + profiles.toString()); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index f7cd39372e11..e83c62d7c222 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -36,7 +36,6 @@ import java.util.List; */ final class SapProfile implements LocalBluetoothProfile { private static final String TAG = "SapProfile"; - private static boolean V = true; private BluetoothSap mService; private boolean mIsProfileReady; @@ -59,7 +58,7 @@ final class SapProfile implements LocalBluetoothProfile { implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (V) Log.d(TAG,"Bluetooth service connected"); + Log.d(TAG, "Bluetooth service connected, profile:" + profile); mService = (BluetoothSap) proxy; // We just bound to the service, so refresh the UI for any connected SAP devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); @@ -81,7 +80,7 @@ final class SapProfile implements LocalBluetoothProfile { } public void onServiceDisconnected(int profile) { - if (V) Log.d(TAG,"Bluetooth service disconnected"); + Log.d(TAG, "Bluetooth service disconnected, profile:" + profile); mProfileManager.callServiceDisconnectedListeners(); mIsProfileReady=false; } @@ -115,50 +114,47 @@ final class SapProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> sinks = mService.getConnectedDevices(); - if (sinks != null) { - for (BluetoothDevice sink : sinks) { - mService.disconnect(sink); - } + if (mService == null) { + return false; } return mService.connect(device); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) { - if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { - mService.setPriority(device, BluetoothProfile.PRIORITY_ON); - } - return mService.disconnect(device); - } else { + if (mService == null) { return false; } + if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { + mService.setPriority(device, BluetoothProfile.PRIORITY_ON); + } + return mService.disconnect(device); } public int getConnectionStatus(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.STATE_DISCONNECTED; - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - - return !deviceList.isEmpty() && deviceList.get(0).equals(device) - ? mService.getConnectionState(device) - : BluetoothProfile.STATE_DISCONNECTED; + if (mService == null) { + return BluetoothProfile.STATE_DISCONNECTED; + } + return mService.getConnectionState(device); } public boolean isPreferred(BluetoothDevice device) { - if (mService == null) return false; + if (mService == null) { + return false; + } return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; } public int getPreferred(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.PRIORITY_OFF; + if (mService == null) { + return BluetoothProfile.PRIORITY_OFF; + } return mService.getPriority(device); } public void setPreferred(BluetoothDevice device, boolean preferred) { - if (mService == null) return; + if (mService == null) { + return; + } if (preferred) { if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { mService.setPriority(device, BluetoothProfile.PRIORITY_ON); @@ -169,7 +165,9 @@ final class SapProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getConnectedDevices() { - if (mService == null) return new ArrayList<BluetoothDevice>(0); + if (mService == null) { + return new ArrayList<BluetoothDevice>(0); + } return mService.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, @@ -207,11 +205,11 @@ final class SapProfile implements LocalBluetoothProfile { } protected void finalize() { - if (V) Log.d(TAG, "finalize()"); + Log.d(TAG, "finalize()"); if (mService != null) { try { BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP, - mService); + mService); mService = null; }catch (Throwable t) { Log.w(TAG, "Error cleaning up SAP proxy", t); diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java index 42306f6d46d0..9870b3c9d754 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java @@ -107,12 +107,13 @@ class LicenseHtmlGeneratorFromXml { mXmlFiles = xmlFiles; } - public static boolean generateHtml(List<File> xmlFiles, File outputFile) { + public static boolean generateHtml(List<File> xmlFiles, File outputFile, + String noticeHeader) { LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles); - return genertor.generateHtml(outputFile); + return genertor.generateHtml(outputFile, noticeHeader); } - private boolean generateHtml(File outputFile) { + private boolean generateHtml(File outputFile, String noticeHeader) { for (File xmlFile : mXmlFiles) { parse(xmlFile); } @@ -125,7 +126,8 @@ class LicenseHtmlGeneratorFromXml { try { writer = new PrintWriter(outputFile); - generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer); + generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer, + noticeHeader); writer.flush(); writer.close(); @@ -239,13 +241,18 @@ class LicenseHtmlGeneratorFromXml { @VisibleForTesting static void generateHtml(Map<String, String> fileNameToContentIdMap, - Map<String, String> contentIdToFileContentMap, PrintWriter writer) { + Map<String, String> contentIdToFileContentMap, PrintWriter writer, + String noticeHeader) { List<String> fileNameList = new ArrayList(); fileNameList.addAll(fileNameToContentIdMap.keySet()); Collections.sort(fileNameList); writer.println(HTML_HEAD_STRING); + if (!TextUtils.isEmpty(noticeHeader)) { + writer.println(noticeHeader); + } + int count = 0; Map<String, Integer> contentIdToOrderMap = new HashMap(); List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList(); diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java index 2aaea657d29f..8b562f7066c3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java @@ -21,6 +21,7 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settingslib.R; import com.android.settingslib.utils.AsyncLoader; import java.io.File; @@ -108,6 +109,7 @@ public class LicenseHtmlLoader extends AsyncLoader<File> { @VisibleForTesting boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) { - return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile); + return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile, + mContext.getString(R.string.notice_header)); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java new file mode 100644 index 000000000000..6ff3e7085b3c --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSap; +import android.bluetooth.BluetoothProfile; +import android.content.Context; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class SapProfileTest { + + @Mock + private LocalBluetoothAdapter mAdapter; + @Mock + private CachedBluetoothDeviceManager mDeviceManager; + @Mock + private LocalBluetoothProfileManager mProfileManager; + @Mock + private BluetoothSap mService; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + private BluetoothProfile.ServiceListener mServiceListener; + private SapProfile mProfile; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + doAnswer((invocation) -> { + mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1]; + return null; + }).when(mAdapter).getProfileProxy(any(Context.class), + any(BluetoothProfile.ServiceListener.class), eq(BluetoothProfile.SAP)); + + mProfile = new SapProfile(RuntimeEnvironment.application, mAdapter, + mDeviceManager, mProfileManager); + mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService); + } + + @Test + public void connect_shouldConnectBluetoothSap() { + mProfile.connect(mBluetoothDevice); + verify(mService).connect(mBluetoothDevice); + } + + @Test + public void disconnect_shouldDisconnectBluetoothSap() { + mProfile.disconnect(mBluetoothDevice); + verify(mService).disconnect(mBluetoothDevice); + } + + @Test + public void getConnectionStatus_shouldReturnConnectionState() { + when(mService.getConnectionState(mBluetoothDevice)). + thenReturn(BluetoothProfile.STATE_CONNECTED); + assertThat(mProfile.getConnectionStatus(mBluetoothDevice)). + isEqualTo(BluetoothProfile.STATE_CONNECTED); + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java index 96b2a1433f53..b00476b24921 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java @@ -50,7 +50,7 @@ public class LicenseHtmlGeneratorFromXmlTest { + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n" + "</licenses2>"; - private static final String EXPECTED_HTML_STRING = + private static final String HTML_HEAD_STRING = "<html><head>\n" + "<style type=\"text/css\">\n" + "body { padding: 0; font-family: sans-serif; }\n" @@ -63,8 +63,12 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</head>" + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n" + "<div class=\"toc\">\n" - + "<ul>\n" - + "<li><a href=\"#id0\">/file0</a></li>\n" + + "<ul>\n"; + + private static final String HTML_CUSTOM_HEADING = "Custom heading"; + + private static final String HTML_BODY_STRING = + "<li><a href=\"#id0\">/file0</a></li>\n" + "<li><a href=\"#id0\">/file1</a></li>\n" + "</ul>\n" + "</div><!-- table of contents -->\n" @@ -81,6 +85,11 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</td></tr><!-- same-license -->\n" + "</table></body></html>\n"; + private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING; + + private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING = + HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING; + @Test public void testParseValidXmlStream() throws XmlPullParserException, IOException { Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); @@ -117,7 +126,23 @@ public class LicenseHtmlGeneratorFromXmlTest { StringWriter output = new StringWriter(); LicenseHtmlGeneratorFromXml.generateHtml( - fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output)); + fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), ""); assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING); } + + @Test + public void testGenerateHtmlWithCustomHeading() { + Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); + Map<String, String> contentIdToFileContentMap = new HashMap<String, String>(); + + fileNameToContentIdMap.put("/file0", "0"); + fileNameToContentIdMap.put("/file1", "0"); + contentIdToFileContentMap.put("0", "license content #0"); + + StringWriter output = new StringWriter(); + LicenseHtmlGeneratorFromXml.generateHtml( + fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), + HTML_CUSTOM_HEADING); + assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING); + } } diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 4fc190d43056..2530abc765da 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -16,6 +16,8 @@ package com.android.shell; +import static android.content.pm.PackageManager.FEATURE_LEANBACK; +import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static com.android.shell.BugreportPrefs.STATE_HIDE; @@ -42,6 +44,7 @@ import java.util.zip.ZipOutputStream; import libcore.io.Streams; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ChooserActivity; import com.android.internal.logging.MetricsLogger; @@ -232,9 +235,11 @@ public class BugreportProgressService extends Service { */ private boolean mTakingScreenshot; + @GuardedBy("sNotificationBundle") private static final Bundle sNotificationBundle = new Bundle(); private boolean mIsWatch; + private boolean mIsTv; private int mLastProgressPercent; @@ -255,6 +260,9 @@ public class BugreportProgressService extends Service { final Configuration conf = mContext.getResources().getConfiguration(); mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH; + PackageManager packageManager = getPackageManager(); + mIsTv = packageManager.hasSystemFeature(FEATURE_LEANBACK) + || packageManager.hasSystemFeature(FEATURE_TELEVISION); NotificationManager nm = NotificationManager.from(mContext); nm.createNotificationChannel( new NotificationChannel(NOTIFICATION_CHANNEL_ID, @@ -500,8 +508,8 @@ public class BugreportProgressService extends Service { .setProgress(info.max, info.progress, false) .setOngoing(true); - // Wear bugreport doesn't need the bug info dialog, screenshot and cancel action. - if (!mIsWatch) { + // Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action. + if (!(mIsWatch || mIsTv)) { final Action cancelAction = new Action.Builder(null, mContext.getString( com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build(); final Intent infoIntent = new Intent(mContext, BugreportProgressService.class); @@ -1053,10 +1061,12 @@ public class BugreportProgressService extends Service { } private static Notification.Builder newBaseNotification(Context context) { - if (sNotificationBundle.isEmpty()) { - // Rename notifcations from "Shell" to "Android System" - sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, - context.getString(com.android.internal.R.string.android_system_label)); + synchronized (sNotificationBundle) { + if (sNotificationBundle.isEmpty()) { + // Rename notifcations from "Shell" to "Android System" + sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, + context.getString(com.android.internal.R.string.android_system_label)); + } } return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID) .addExtras(sNotificationBundle) diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java index 0734e0dae790..4fbc22663675 100644 --- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java +++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java @@ -20,6 +20,7 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.net.Uri; +import android.os.Bundle; import android.os.CancellationSignal; import android.os.FileUtils; import android.os.ParcelFileDescriptor; @@ -68,6 +69,18 @@ public class BugreportStorageProvider extends FileSystemProvider { } @Override + public Cursor queryChildDocuments( + String parentDocumentId, String[] projection, String sortOrder) + throws FileNotFoundException { + final Cursor c = super.queryChildDocuments(parentDocumentId, projection, sortOrder); + final Bundle extras = new Bundle(); + extras.putCharSequence(DocumentsContract.EXTRA_INFO, + getContext().getText(R.string.bugreport_confirm)); + c.setExtras(extras); + return c; + } + + @Override public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { if (DOC_ID_ROOT.equals(documentId)) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java index cd831d1e31f6..0c498bb8d2c7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java @@ -24,8 +24,6 @@ import android.util.DisplayMetrics; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import sun.misc.Resource; - public class NavigationBarCompat { /** * Touch slopes and thresholds for quick step operations. Drag slop is the point where the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 2031b27c93f2..1fefa18ad631 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -53,6 +53,7 @@ public class ZenModeControllerImpl extends CurrentUserTracker implements ZenMode private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); + private final Object mCallbacksLock = new Object(); private final Context mContext; private final GlobalSetting mModeSetting; private final GlobalSetting mConfigSetting; @@ -113,16 +114,20 @@ public class ZenModeControllerImpl extends CurrentUserTracker implements ZenMode @Override public void addCallback(Callback callback) { - if (callback == null) { - Slog.e(TAG, "Attempted to add a null callback."); - return; + synchronized (mCallbacksLock) { + if (callback == null) { + Slog.e(TAG, "Attempted to add a null callback."); + return; + } + mCallbacks.add(callback); } - mCallbacks.add(callback); } @Override public void removeCallback(Callback callback) { - mCallbacks.remove(callback); + synchronized (mCallbacksLock) { + mCallbacks.remove(callback); + } } @Override @@ -186,28 +191,40 @@ public class ZenModeControllerImpl extends CurrentUserTracker implements ZenMode } private void fireNextAlarmChanged() { - Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged()); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged()); + } } private void fireEffectsSuppressorChanged() { - Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged()); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged()); + } } private void fireZenChanged(int zen) { - Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen)); + } } private void fireZenAvailableChanged(boolean available) { - Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available)); + } } private void fireManualRuleChanged(ZenRule rule) { - Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule)); + } } @VisibleForTesting protected void fireConfigChanged(ZenModeConfig config) { - Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config)); + } } @VisibleForTesting diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e41a09ef672e..5e8ffb79c493 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -57,6 +57,7 @@ import android.net.ConnectivityManager; import android.net.ConnectivityManager.PacketKeepalive; import android.net.IConnectivityManager; import android.net.IIpConnectivityMetrics; +import android.net.INetd; import android.net.INetdEventCallback; import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; @@ -88,6 +89,7 @@ import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; import android.net.util.MultinetworkPolicyTracker; +import android.net.util.NetdService; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -259,7 +261,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; - private INetworkManagementService mNetd; + private INetworkManagementService mNMS; + private INetd mNetd; private INetworkStatsService mStatsService; private INetworkPolicyManager mPolicyManager; private NetworkPolicyManagerInternal mPolicyManagerInternal; @@ -390,9 +393,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_PROMPT_UNVALIDATED = 29; /** - * used internally to (re)configure mobile data always-on settings. + * used internally to (re)configure always-on networks. */ - private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30; + private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30; /** * used to add a network listener with a pending intent @@ -748,6 +751,12 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultMobileDataRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); + // The default WiFi request is a background request so that apps using WiFi are + // migrated to a better network (typically ethernet) when one comes up, instead + // of staying on WiFi forever. + mDefaultWifiRequest = createDefaultInternetRequestForTransport( + NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST); + mHandlerThread = new HandlerThread("ConnectivityServiceThread"); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); @@ -759,7 +768,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); mContext = checkNotNull(context, "missing Context"); - mNetd = checkNotNull(netManager, "missing INetworkManagementService"); + mNMS = checkNotNull(netManager, "missing INetworkManagementService"); mStatsService = checkNotNull(statsService, "missing INetworkStatsService"); mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager"); mPolicyManagerInternal = checkNotNull( @@ -767,6 +776,7 @@ public class ConnectivityService extends IConnectivityManager.Stub "missing NetworkPolicyManagerInternal"); mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED); + mNetd = NetdService.getInstance(); mKeyStore = KeyStore.getInstance(); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); @@ -849,7 +859,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mTethering = makeTethering(); - mPermissionMonitor = new PermissionMonitor(mContext, mNetd); + mPermissionMonitor = new PermissionMonitor(mContext, mNMS); //set up the listener for user state for creating user VPNs IntentFilter intentFilter = new IntentFilter(); @@ -864,8 +874,8 @@ public class ConnectivityService extends IConnectivityManager.Stub new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); try { - mNetd.registerObserver(mTethering); - mNetd.registerObserver(mDataActivityObserver); + mNMS.registerObserver(mTethering); + mNMS.registerObserver(mDataActivityObserver); } catch (RemoteException e) { loge("Error registering observer :" + e); } @@ -896,7 +906,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler); - mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties); + mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties); registerPrivateDnsSettingsCallbacks(); } @@ -912,7 +922,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return mDefaultRequest; } }; - return new Tethering(mContext, mNetd, mStatsService, mPolicyManager, + return new Tethering(mContext, mNMS, mStatsService, mPolicyManager, IoThread.get().getLooper(), new MockableSystemProperties(), deps); } @@ -944,8 +954,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it // by subclassing SettingsObserver. @VisibleForTesting - void updateMobileDataAlwaysOn() { - mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + void updateAlwaysOnNetworks() { + mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } // See FakeSettingsProvider comment above. @@ -954,22 +964,30 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED); } - private void handleMobileDataAlwaysOn() { + private void handleAlwaysOnNetworkRequest( + NetworkRequest networkRequest, String settingName, boolean defaultValue) { final boolean enable = toBool(Settings.Global.getInt( - mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1)); - final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null); + mContext.getContentResolver(), settingName, encodeBool(defaultValue))); + final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null); if (enable == isEnabled) { return; // Nothing to do. } if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - null, mDefaultMobileDataRequest, new Binder())); + null, networkRequest, new Binder())); } else { - handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID); + handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID); } } + private void handleConfigureAlwaysOnNetworks() { + handleAlwaysOnNetworkRequest( + mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true); + handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED, + false); + } + private void registerSettingsCallbacks() { // Watch for global HTTP proxy changes. mSettingsObserver.observe( @@ -979,7 +997,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // Watch for whether or not to keep mobile data always on. mSettingsObserver.observe( Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON), - EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); + + // Watch for whether or not to keep wifi always on. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED), + EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } private void registerPrivateDnsSettingsCallbacks() { @@ -1476,6 +1499,20 @@ public class ConnectivityService extends IConnectivityManager.Stub }; /** + * Ensures that the system cannot call a particular method. + */ + private boolean disallowedBecauseSystemCaller() { + // TODO: start throwing a SecurityException when GnssLocationProvider stops calling + // requestRouteToHost. + if (isSystem(Binder.getCallingUid())) { + log("This method exists only for app backwards compatibility" + + " and must not be called by system services."); + return true; + } + return false; + } + + /** * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. * @param networkType the type of the network over which traffic to the @@ -1486,6 +1523,9 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) { + if (disallowedBecauseSystemCaller()) { + return false; + } enforceChangePermission(); if (mProtectedNetworks.contains(networkType)) { enforceConnectivityInternalPermission(); @@ -1563,7 +1603,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("Adding legacy route " + bestRoute + " for UID/PID " + uid + "/" + Binder.getCallingPid()); try { - mNetd.addLegacyRouteForNetId(netId, bestRoute, uid); + mNMS.addLegacyRouteForNetId(netId, bestRoute, uid); } catch (Exception e) { // never crash - catch them all if (DBG) loge("Exception trying to add a route: " + e); @@ -1797,7 +1837,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } void systemReady() { - loadGlobalProxy(); + mProxyTracker.loadGlobalProxy(); registerNetdEventCallback(); synchronized (this) { @@ -1814,8 +1854,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // for user to unlock device too. updateLockdownVpn(); - // Configure whether mobile data is always on. - mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON)); + // Create network requests for always-on networks. + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS)); mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY)); @@ -1853,7 +1893,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) { try { - mNetd.addIdleTimer(iface, timeout, type); + mNMS.addIdleTimer(iface, timeout, type); } catch (Exception e) { // You shall not crash! loge("Exception in setupDataActivityTracking " + e); @@ -1872,7 +1912,7 @@ public class ConnectivityService extends IConnectivityManager.Stub caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) { try { // the call fails silently if no idle timer setup for this interface - mNetd.removeIdleTimer(iface); + mNMS.removeIdleTimer(iface); } catch (Exception e) { loge("Exception in removeDataActivityTracking " + e); } @@ -1880,6 +1920,18 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Update data activity tracking when network state is updated. + */ + private void updateDataActivityTracking(NetworkAgentInfo newNetwork, + NetworkAgentInfo oldNetwork) { + if (newNetwork != null) { + setupDataActivityTracking(newNetwork); + } + if (oldNetwork != null) { + removeDataActivityTracking(oldNetwork); + } + } + /** * Reads the network specific MTU size from resources. * and set it on it's iface. */ @@ -1907,7 +1959,7 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (VDBG) log("Setting MTU size: " + iface + ", " + mtu); - mNetd.setMtu(iface, mtu); + mNMS.setMtu(iface, mtu); } catch (Exception e) { Slog.e(TAG, "exception in setMtu()" + e); } @@ -2561,7 +2613,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } nai.clearLingerState(); if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { - removeDataActivityTracking(nai); + updateDataActivityTracking(null /* newNetwork */, nai); notifyLockdownVpn(nai); ensureNetworkTransitionWakelock(nai.name()); } @@ -2581,7 +2633,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // NetworkFactories, so network traffic isn't interrupted for an unnecessarily // long time. try { - mNetd.removeNetwork(nai.network.netId); + mNMS.removeNetwork(nai.network.netId); } catch (Exception e) { loge("Exception removing network: " + e); } @@ -2779,20 +2831,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // TODO: remove this code once we know that the Slog.wtf is never hit. - // - // Find all networks that are satisfying this request and remove the request - // from their request lists. - // TODO - it's my understanding that for a request there is only a single - // network satisfying it, so this loop is wasteful - for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) { - if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) { - Slog.wtf(TAG, "Request " + nri.request + " satisfied by " + - otherNai.name() + ", but mNetworkAgentInfos says " + - (nai != null ? nai.name() : "null")); - } - } - // Maintain the illusion. When this request arrived, we might have pretended // that a network connected to serve it, even though the network was already // connected. Now that this request has gone away, we might have to pretend @@ -3106,8 +3144,8 @@ public class ConnectivityService extends IConnectivityManager.Stub handlePromptUnvalidated((Network) msg.obj); break; } - case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: { - handleMobileDataAlwaysOn(); + case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: { + handleConfigureAlwaysOnNetworks(); break; } // Sent by KeepaliveTracker to process an app request on the state machine thread. @@ -3417,31 +3455,6 @@ public class ConnectivityService extends IConnectivityManager.Stub mProxyTracker.setGlobalProxy(proxyProperties); } - private void loadGlobalProxy() { - ContentResolver res = mContext.getContentResolver(); - String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST); - int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0); - String exclList = Settings.Global.getString(res, - Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST); - String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC); - if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) { - ProxyInfo proxyProperties; - if (!TextUtils.isEmpty(pacFileUrl)) { - proxyProperties = new ProxyInfo(pacFileUrl); - } else { - proxyProperties = new ProxyInfo(host, port, exclList); - } - if (!proxyProperties.isValid()) { - if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString()); - return; - } - - synchronized (mProxyTracker.mProxyLock) { - mProxyTracker.mGlobalProxy = proxyProperties; - } - } - } - @Override @Nullable public ProxyInfo getGlobalProxy() { @@ -3760,7 +3773,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown"); return false; } - setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile)); + setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile)); } else { setLockdownTracker(null); } @@ -4015,7 +4028,7 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("Starting user already has a VPN"); return; } - userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId); + userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId); mVpns.put(userId, userVpn); if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { updateLockdownVpn(); @@ -4535,6 +4548,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like Wi-Fi are active. private final NetworkRequest mDefaultMobileDataRequest; + // Request used to optionally keep wifi data active even when higher + // priority networks like ethernet are active. + private final NetworkRequest mDefaultWifiRequest; + private NetworkAgentInfo getNetworkForRequest(int requestId) { synchronized (mNetworkForRequestId) { return mNetworkForRequestId.get(requestId); @@ -4632,7 +4649,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDnsManager.updatePrivateDnsStatus(netId, newLp); // Start or stop clat accordingly to network state. - networkAgent.updateClat(mNetd); + networkAgent.updateClat(mNMS); if (isDefaultNetwork(networkAgent)) { handleApplyDefaultProxy(newLp.getHttpProxy()); } else { @@ -4671,9 +4688,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final String prefix = "iface:" + iface; try { if (add) { - mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask); + mNetd.wakeupAddInterface(iface, prefix, mark, mask); } else { - mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask); + mNetd.wakeupDelInterface(iface, prefix, mark, mask); } } catch (Exception e) { loge("Exception modifying wakeup packet monitoring: " + e); @@ -4689,7 +4706,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (String iface : interfaceDiff.added) { try { if (DBG) log("Adding iface " + iface + " to network " + netId); - mNetd.addInterfaceToNetwork(iface, netId); + mNMS.addInterfaceToNetwork(iface, netId); wakeupModifyInterface(iface, caps, true); } catch (Exception e) { loge("Exception adding interface: " + e); @@ -4699,7 +4716,7 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (DBG) log("Removing iface " + iface + " from network " + netId); wakeupModifyInterface(iface, caps, false); - mNetd.removeInterfaceFromNetwork(iface, netId); + mNMS.removeInterfaceFromNetwork(iface, netId); } catch (Exception e) { loge("Exception removing interface: " + e); } @@ -4723,7 +4740,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (route.hasGateway()) continue; if (VDBG) log("Adding Route [" + route + "] to network " + netId); try { - mNetd.addRoute(netId, route); + mNMS.addRoute(netId, route); } catch (Exception e) { if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) { loge("Exception in addRoute for non-gateway: " + e); @@ -4734,7 +4751,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (route.hasGateway() == false) continue; if (VDBG) log("Adding Route [" + route + "] to network " + netId); try { - mNetd.addRoute(netId, route); + mNMS.addRoute(netId, route); } catch (Exception e) { if ((route.getGateway() instanceof Inet4Address) || VDBG) { loge("Exception in addRoute for gateway: " + e); @@ -4745,7 +4762,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (RouteInfo route : routeDiff.removed) { if (VDBG) log("Removing Route [" + route + "] from network " + netId); try { - mNetd.removeRoute(netId, route); + mNMS.removeRoute(netId, route); } catch (Exception e) { loge("Exception in removeRoute: " + e); } @@ -4857,7 +4874,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final String newPermission = getNetworkPermission(newNc); if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) { try { - mNetd.setNetworkPermission(nai.network.netId, newPermission); + mNMS.setNetworkPermission(nai.network.netId, newPermission); } catch (RemoteException e) { loge("Exception in setNetworkPermission: " + e); } @@ -4917,12 +4934,12 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!newRanges.isEmpty()) { final UidRange[] addedRangesArray = new UidRange[newRanges.size()]; newRanges.toArray(addedRangesArray); - mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray); + mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray); } if (!prevRanges.isEmpty()) { final UidRange[] removedRangesArray = new UidRange[prevRanges.size()]; prevRanges.toArray(removedRangesArray); - mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray); + mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray); } } catch (Exception e) { // Never crash! @@ -5091,9 +5108,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private void makeDefault(NetworkAgentInfo newNetwork) { if (DBG) log("Switching to new default network: " + newNetwork); - setupDataActivityTracking(newNetwork); + try { - mNetd.setDefaultNetId(newNetwork.network.netId); + mNMS.setDefaultNetId(newNetwork.network.netId); } catch (Exception e) { loge("Exception setting default network :" + e); } @@ -5266,6 +5283,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } if (isNewDefault) { + updateDataActivityTracking(newNetwork, oldDefaultNetwork); // Notify system services that this network is up. makeDefault(newNetwork); // Log 0 -> X and Y -> X default network transitions, where X is the new default. @@ -5488,12 +5506,12 @@ public class ConnectivityService extends IConnectivityManager.Stub try { // This should never fail. Specifying an already in use NetID will cause failure. if (networkAgent.isVPN()) { - mNetd.createVirtualNetwork(networkAgent.network.netId, + mNMS.createVirtualNetwork(networkAgent.network.netId, !networkAgent.linkProperties.getDnsServers().isEmpty(), (networkAgent.networkMisc == null || !networkAgent.networkMisc.allowBypass)); } else { - mNetd.createPhysicalNetwork(networkAgent.network.netId, + mNMS.createPhysicalNetwork(networkAgent.network.netId, getNetworkPermission(networkAgent.networkCapabilities)); } } catch (Exception e) { diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index a69d41683c29..8c25917c7436 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -19,6 +19,8 @@ package com.android.server; import static android.Manifest.permission.DUMP; import static android.net.IpSecManager.INVALID_RESOURCE_ID; import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNSPEC; import static android.system.OsConstants.EINVAL; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; @@ -63,6 +65,8 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -1426,6 +1430,17 @@ public class IpSecService extends IIpSecService.Stub { + "or Encryption algorithms"); } + private int getFamily(String inetAddress) { + int family = AF_UNSPEC; + InetAddress checkAddress = NetworkUtils.numericToInetAddress(inetAddress); + if (checkAddress instanceof Inet4Address) { + family = AF_INET; + } else if (checkAddress instanceof Inet6Address) { + family = AF_INET6; + } + return family; + } + /** * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an * IllegalArgumentException if they are not. @@ -1479,6 +1494,26 @@ public class IpSecService extends IIpSecService.Stub { // Require a valid source address for all transforms. checkInetAddress(config.getSourceAddress()); + // Check to ensure source and destination have the same address family. + String sourceAddress = config.getSourceAddress(); + String destinationAddress = config.getDestinationAddress(); + int sourceFamily = getFamily(sourceAddress); + int destinationFamily = getFamily(destinationAddress); + if (sourceFamily != destinationFamily) { + throw new IllegalArgumentException( + "Source address (" + + sourceAddress + + ") and destination address (" + + destinationAddress + + ") have different address families."); + } + + // Throw an error if UDP Encapsulation is not used in IPv4. + if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) { + throw new IllegalArgumentException( + "UDP Encapsulation is not supported for this address family"); + } + switch (config.getMode()) { case IpSecTransform.MODE_TRANSPORT: break; diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 9c81748ea435..ab0a56516ff4 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -160,6 +160,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub private static final int MAX_UID_RANGES_PER_COMMAND = 10; + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + /** * Name representing {@link #setGlobalAlert(long)} limit when delivered to * {@link INetworkManagementEventObserver#limitReached(String, String)}. @@ -1229,18 +1231,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void startTethering(String[] dhcpRange) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - // cmd is "tether start first_start first_stop second_start second_stop ..." // an odd number of addrs will fail - final Command cmd = new Command("tether", "start"); - for (String d : dhcpRange) { - cmd.appendArg(d); - } - try { - mConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherStart(dhcpRange); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1248,9 +1244,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void stopTethering() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("tether", "stop"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherStop(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1258,25 +1254,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub public boolean isTetheringStarted() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - final NativeDaemonEvent event; try { - event = mConnector.execute("tether", "status"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + final boolean isEnabled = mNetdService.tetherIsEnabled(); + return isEnabled; + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } - - // 210 Tethering services started - event.checkCode(TetherStatusResult); - return event.getMessage().endsWith("started"); } @Override public void tetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("tether", "interface", "add", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherInterfaceAdd(iface); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } List<RouteInfo> routes = new ArrayList<>(); // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it @@ -1289,9 +1281,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void untetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("tether", "interface", "remove", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherInterfaceRemove(iface); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } finally { removeInterfaceFromLocalNetwork(iface); } @@ -1301,11 +1293,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub public String[] listTetheredInterfaces() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - return NativeDaemonEvent.filterMessageList( - mConnector.executeForList("tether", "interface", "list"), - TetherInterfaceListResult); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + final List<String> result = mNetdService.tetherInterfaceList(); + return result.toArray(EMPTY_STRING_ARRAY); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1314,16 +1305,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET; - final Command cmd = new Command("tether", "dns", "set", netId); - - for (String s : dns) { - cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress()); - } try { - mConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherDnsSet(netId, dns); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1331,10 +1317,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub public String[] getDnsForwarders() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - return NativeDaemonEvent.filterMessageList( - mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + final List<String> result = mNetdService.tetherDnsList(); + return result.toArray(EMPTY_STRING_ARRAY); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 0955beda1df5..0f9fe83ba6a5 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1644,6 +1644,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtras(data); // Pass the subscription along with the intent. intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -1698,6 +1699,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); } // If the phoneId is invalid, the broadcast is for overall call state. if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) { diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 59093c19d733..289143c87ac0 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -87,7 +87,7 @@ public class Watchdog extends Thread { "media.metrics", // system/bin/mediametrics "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service "com.android.bluetooth", // Bluetooth service - "statsd", // Stats daemon + "/system/bin/statsd", // Stats daemon }; public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9d0a8653178a..6c9871129ba0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -717,8 +717,6 @@ public class ActivityManagerService extends IActivityManager.Stub // Whether we should use SCHED_FIFO for UI and RenderThreads. private boolean mUseFifoUiScheduling = false; - private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService"; - BroadcastQueue mFgBroadcastQueue; BroadcastQueue mBgBroadcastQueue; // Convenient for easy iteration over the queues. Foreground is first @@ -810,7 +808,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean asProto) { if (asProto) return; doDump(fd, pw, new String[]{"activities"}, asProto); - doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto); + doDump(fd, pw, new String[]{"service", "all-platform-critical"}, asProto); } @Override diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java index dc65e1e69834..b7bbd422458f 100644 --- a/services/core/java/com/android/server/connectivity/ProxyTracker.java +++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java @@ -142,6 +142,35 @@ public class ProxyTracker { } /** + * Read the global proxy settings and cache them in memory. + */ + public void loadGlobalProxy() { + ContentResolver res = mContext.getContentResolver(); + String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST); + int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0); + String exclList = Settings.Global.getString(res, + Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST); + String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC); + if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) { + ProxyInfo proxyProperties; + if (!TextUtils.isEmpty(pacFileUrl)) { + proxyProperties = new ProxyInfo(pacFileUrl); + } else { + proxyProperties = new ProxyInfo(host, port, exclList); + } + if (!proxyProperties.isValid()) { + if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties); + return; + } + + synchronized (mProxyLock) { + mGlobalProxy = proxyProperties; + } + } + // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ? + } + + /** * Sends the system broadcast informing apps about a new proxy configuration. * * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 2a80f0e7c291..48082b64ddfc 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -163,8 +163,8 @@ public class Vpn { // TODO: create separate trackers for each unique VPN to support // automated reconnection - private Context mContext; - private NetworkInfo mNetworkInfo; + private final Context mContext; + private final NetworkInfo mNetworkInfo; private String mPackage; private int mOwnerUID; private String mInterface; diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index 2b1d9196fe18..1e6bb04858a1 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -320,9 +320,8 @@ public class TetheringConfiguration { } private static boolean getEnableLegacyDhcpServer(Context ctx) { - // TODO: make the default false (0) and update javadoc in Settings.java final ContentResolver cr = ctx.getContentResolver(); - final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); + final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); return intVal != 0; } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 60e9eaab5721..3f031699bd7a 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -159,8 +159,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); + // Perform polling and persist all (FLAG_PERSIST_ALL). private static final int MSG_PERFORM_POLL = 1; - private static final int MSG_REGISTER_GLOBAL_ALERT = 2; + // Perform polling, persist network, and register the global alert again. + private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2; /** Flags to control detail level of poll event. */ private static final int FLAG_PERSIST_NETWORK = 0x1; @@ -168,6 +170,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID; private static final int FLAG_PERSIST_FORCE = 0x100; + /** + * When global alert quota is high, wait for this delay before processing each polling, + * and do not schedule further polls once there is already one queued. + * This avoids firing the global alert too often on devices with high transfer speeds and + * high quota. + */ + private static final int PERFORM_POLL_DELAY_MS = 1000; + private static final String TAG_NETSTATS_ERROR = "netstats_error"; private final Context mContext; @@ -920,7 +930,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } // Create baseline stats - mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL, FLAG_PERSIST_ALL)); + mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL)); return normalizedRequest; } @@ -1055,13 +1065,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); if (LIMIT_GLOBAL_ALERT.equals(limitName)) { - // kick off background poll to collect network stats; UID stats - // are handled during normal polling interval. - final int flags = FLAG_PERSIST_NETWORK; - mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget(); - - // re-arm global alert for next update - mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget(); + // kick off background poll to collect network stats unless there is already + // such a call pending; UID stats are handled during normal polling interval. + if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) { + mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT, + PERFORM_POLL_DELAY_MS); + } } } }; @@ -1673,11 +1682,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_PERFORM_POLL: { - final int flags = msg.arg1; - mService.performPoll(flags); + mService.performPoll(FLAG_PERSIST_ALL); return true; } - case MSG_REGISTER_GLOBAL_ALERT: { + case MSG_PERFORM_POLL_REGISTER_ALERT: { + mService.performPoll(FLAG_PERSIST_NETWORK); mService.registerGlobalAlert(); return true; } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 833cc5bd91f2..1fb51b7fa0e8 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -16,6 +16,8 @@ package com.android.server.pm.dex; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Context; @@ -57,8 +59,6 @@ import dalvik.system.DexFile; import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import libcore.util.NonNull; -import libcore.util.Nullable; import java.io.File; import java.io.FileNotFoundException; diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING new file mode 100644 index 000000000000..ad5255904d20 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING @@ -0,0 +1,22 @@ +{ + "presubmit": [ + { + "name": "DexLoggerTests" + }, + { + "name": "DexManagerTests" + }, + { + "name": "DexoptOptionsTests" + }, + { + "name": "DexoptUtilsTest" + }, + { + "name": "PackageDexUsageTests" + }, + { + "name": "DexLoggerIntegrationTests" + } + ] +} diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 89efe12927ba..306f73aa704b 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -98,7 +98,6 @@ cc_defaults { "libsensorservicehidl", "libgui", "libusbhost", - "libsuspend", "libtinyalsa", "libEGL", "libGLESv2", @@ -130,6 +129,7 @@ cc_defaults { "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", + "android.system.suspend@1.0", ], static_libs: [ diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 02ad6c71b586..0ff60e44b0ce 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -30,6 +30,8 @@ #include <android/hardware/power/1.0/IPower.h> #include <android/hardware/power/1.1/IPower.h> +#include <android/system/suspend/1.0/ISystemSuspend.h> +#include <android/system/suspend/1.0/ISystemSuspendCallback.h> #include <android_runtime/AndroidRuntime.h> #include <jni.h> @@ -39,7 +41,6 @@ #include <log/log.h> #include <utils/misc.h> #include <utils/Log.h> -#include <suspend/autosuspend.h> using android::hardware::Return; using android::hardware::Void; @@ -49,6 +50,8 @@ using android::hardware::power::V1_0::Status; using android::hardware::power::V1_1::PowerStateSubsystem; using android::hardware::power::V1_1::PowerStateSubsystemSleepState; using android::hardware::hidl_vec; +using android::system::suspend::V1_0::ISystemSuspend; +using android::system::suspend::V1_0::ISystemSuspendCallback; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_0 = android::hardware::power::V1_0::IPower; @@ -63,6 +66,7 @@ static sem_t wakeup_sem; extern sp<IPowerV1_0> getPowerHalV1_0(); extern sp<IPowerV1_1> getPowerHalV1_1(); extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName); +extern sp<ISystemSuspend> getSuspendHal(); // Java methods used in getLowPowerStats static jmethodID jgetAndUpdatePlatformState = NULL; @@ -70,16 +74,19 @@ static jmethodID jgetSubsystem = NULL; static jmethodID jputVoter = NULL; static jmethodID jputState = NULL; -static void wakeup_callback(bool success) -{ - ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); - int ret = sem_post(&wakeup_sem); - if (ret < 0) { - char buf[80]; - strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error posting wakeup sem: %s\n", buf); +class WakeupCallback : public ISystemSuspendCallback { +public: + Return<void> notifyWakeup(bool success) override { + ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); + int ret = sem_post(&wakeup_sem); + if (ret < 0) { + char buf[80]; + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error posting wakeup sem: %s\n", buf); + } + return Void(); } -} +}; static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) { @@ -101,11 +108,14 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) return -1; } ALOGV("Registering callback..."); - autosuspend_set_wakeup_callback(&wakeup_callback); + sp<ISystemSuspend> suspendHal = getSuspendHal(); + suspendHal->registerCallback(new WakeupCallback()); } // Wait for wakeup. ALOGV("Waiting for wakeup..."); + // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup + // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events. int ret = sem_wait(&wakeup_sem); if (ret < 0) { char buf[80]; diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index b2d35d4153a0..0c9b5f4999a0 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -19,6 +19,7 @@ //#define LOG_NDEBUG 0 #include <android/hardware/power/1.1/IPower.h> +#include <android/system/suspend/1.0/ISystemSuspend.h> #include <nativehelper/JNIHelp.h> #include "jni.h" @@ -35,7 +36,7 @@ #include <utils/Log.h> #include <hardware/power.h> #include <hardware_legacy/power.h> -#include <suspend/autosuspend.h> +#include <hidl/ServiceManagement.h> #include "com_android_server_power_PowerManagerService.h" @@ -44,6 +45,9 @@ using android::hardware::Void; using android::hardware::power::V1_0::PowerHint; using android::hardware::power::V1_0::Feature; using android::String8; +using android::system::suspend::V1_0::ISystemSuspend; +using android::system::suspend::V1_0::IWakeLock; +using android::system::suspend::V1_0::WakeLockType; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_0 = android::hardware::power::V1_0::IPower; @@ -171,6 +175,46 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t } } +static sp<ISystemSuspend> gSuspendHal = nullptr; +static sp<IWakeLock> gSuspendBlocker = nullptr; +static std::mutex gSuspendMutex; + +// Assume SystemSuspend HAL is always alive. +// TODO: Force device to restart if SystemSuspend HAL dies. +sp<ISystemSuspend> getSuspendHal() { + static std::once_flag suspendHalFlag; + std::call_once(suspendHalFlag, [](){ + ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default"); + gSuspendHal = ISystemSuspend::getService(); + assert(gSuspendHal != nullptr); + }); + return gSuspendHal; +} + +void enableAutoSuspend() { + static bool enabled = false; + + std::lock_guard<std::mutex> lock(gSuspendMutex); + if (!enabled) { + sp<ISystemSuspend> suspendHal = getSuspendHal(); + suspendHal->enableAutosuspend(); + enabled = true; + } + if (gSuspendBlocker) { + gSuspendBlocker->release(); + gSuspendBlocker.clear(); + } +} + +void disableAutoSuspend() { + std::lock_guard<std::mutex> lock(gSuspendMutex); + if (!gSuspendBlocker) { + sp<ISystemSuspend> suspendHal = getSuspendHal(); + gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL, + "PowerManager.SuspendLockout"); + } +} + // ---------------------------------------------------------------------------- static void nativeInit(JNIEnv* env, jobject obj) { @@ -207,13 +251,13 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { if (enable) { android::base::Timer t; - autosuspend_enable(); + enableAutoSuspend(); if (t.duration() > 100ms) { ALOGD("Excessive delay in autosuspend_enable() while turning screen off"); } } else { android::base::Timer t; - autosuspend_disable(); + disableAutoSuspend(); if (t.duration() > 100ms) { ALOGD("Excessive delay in autosuspend_disable() while turning screen on"); } diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp index 05052047e0fb..47790ce68dc1 100644 --- a/services/devicepolicy/Android.bp +++ b/services/devicepolicy/Android.bp @@ -3,7 +3,6 @@ java_library_static { srcs: ["java/**/*.java"], libs: [ - "conscrypt", "services.core", ], } diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index 77a3e2102452..6ba7d94117b1 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -1,5 +1,8 @@ package android.net.dhcp; +import static android.net.util.NetworkConstants.IPV4_MAX_MTU; +import static android.net.util.NetworkConstants.IPV4_MIN_MTU; + import android.annotation.Nullable; import android.net.DhcpResults; import android.net.LinkAddress; @@ -381,6 +384,26 @@ public abstract class DhcpPacket { } /** + * Returns whether a parameter is included in the parameter request list option of this packet. + * + * <p>If there is no parameter request list option in the packet, false is returned. + * + * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}. + */ + public boolean hasRequestedParam(byte paramId) { + if (mRequestedParams == null) { + return false; + } + + for (byte reqParam : mRequestedParams) { + if (reqParam == paramId) { + return true; + } + } + return false; + } + + /** * Creates a new L3 packet (including IP header) containing the * DHCP udp packet. This method relies upon the delegated method * finishPacket() to insert the per-packet contents. @@ -696,7 +719,11 @@ public abstract class DhcpPacket { addTlv(buf, DHCP_ROUTER, mGateways); addTlv(buf, DHCP_DNS_SERVER, mDnsServers); addTlv(buf, DHCP_DOMAIN_NAME, mDomainName); + addTlv(buf, DHCP_HOST_NAME, mHostName); addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo); + if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) { + addTlv(buf, DHCP_MTU, mMtu); + } } /** @@ -1259,7 +1286,8 @@ public abstract class DhcpPacket { boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, - Inet4Address dhcpServerIdentifier, String domainName, boolean metered) { + Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, + short mtu) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, INADDR_ANY /* clientIp */, yourIp, mac); @@ -1267,9 +1295,11 @@ public abstract class DhcpPacket { pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; + pkt.mHostName = hostname; pkt.mServerIdentifier = dhcpServerIdentifier; pkt.mSubnetMask = netMask; pkt.mBroadcastAddress = bcAddr; + pkt.mMtu = mtu; if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } @@ -1283,7 +1313,8 @@ public abstract class DhcpPacket { boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, - Inet4Address dhcpServerIdentifier, String domainName, boolean metered) { + Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, + short mtu) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp, mac); @@ -1291,9 +1322,11 @@ public abstract class DhcpPacket { pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; + pkt.mHostName = hostname; pkt.mSubnetMask = netMask; pkt.mServerIdentifier = dhcpServerIdentifier; pkt.mBroadcastAddress = bcAddr; + pkt.mMtu = mtu; if (metered) { pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; } diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java index 2b3d577b0eae..cee6fa96bbc5 100644 --- a/services/net/java/android/net/dhcp/DhcpServer.java +++ b/services/net/java/android/net/dhcp/DhcpServer.java @@ -20,6 +20,7 @@ import static android.net.NetworkUtils.getBroadcastAddress; import static android.net.NetworkUtils.getPrefixMaskAsInet4Address; import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER; import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; +import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; import static android.net.dhcp.DhcpPacket.DHCP_SERVER; import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; @@ -46,6 +47,7 @@ import android.os.Message; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; +import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; @@ -350,6 +352,19 @@ public class DhcpServer { return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr())); } + /** + * Get the hostname from a lease if non-empty and requested in the incoming request. + * @param request The incoming request. + * @return The hostname, or null if not requested or empty. + */ + @Nullable + private static String getHostnameIfRequested(@NonNull DhcpPacket request, + @NonNull DhcpLease lease) { + return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname()) + ? lease.getHostname() + : null; + } + private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease, @NonNull MacAddress clientMac) { final boolean broadcastFlag = getBroadcastFlag(request, lease); @@ -358,12 +373,14 @@ public class DhcpServer { getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength()); final Inet4Address broadcastAddr = getBroadcastAddress( mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength()); + final String hostname = getHostnameIfRequested(request, lease); final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket( ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask, broadcastAddr, new ArrayList<>(mServingParams.defaultRouters), new ArrayList<>(mServingParams.dnsServers), - mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered); + mServingParams.getServerInet4Addr(), null /* domainName */, hostname, + mServingParams.metered, (short) mServingParams.linkMtu); return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag); } @@ -374,13 +391,15 @@ public class DhcpServer { // transmitOffer above final boolean broadcastFlag = getBroadcastFlag(request, lease); final int timeout = getLeaseTimeout(lease); + final String hostname = getHostnameIfRequested(request, lease); final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp, lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout, mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(), new ArrayList<>(mServingParams.defaultRouters), new ArrayList<>(mServingParams.dnsServers), - mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered); + mServingParams.getServerInet4Addr(), null /* domainName */, hostname, + mServingParams.metered, (short) mServingParams.linkMtu); return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag); } diff --git a/services/tests/runtests.py b/services/tests/runtests.py index 7980dc23e8e2..f19cc5d567ec 100755 --- a/services/tests/runtests.py +++ b/services/tests/runtests.py @@ -22,8 +22,7 @@ INSTRUMENTED_PACKAGE_RUNNER = ('com.android.frameworks.servicestests/' 'android.support.test.runner.AndroidJUnitRunner') PACKAGE_WHITELIST = ( - 'android.net', - 'com.android.server.connectivity', + "com.android.server", ) COLOR_RED = '\033[0;31m' @@ -37,14 +36,27 @@ def run(shell_command, echo=True): COLOR_NONE) return subprocess.check_call(shell_command, shell=True) - +# usage: +# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py : run tests in com.android.server +# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e package [package name, e.g. com.android.server] +# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e class [class name, e.g. com.android.server.MountServiceTests] +# +# The available INSTRUMENTED_PACKAGE_RUNNER may differ in different environments. +# In this case, use "adb shell pm list instrumentation" to query available runners +# and use the environment variable INSTRUMENTED_PACKAGE_RUNNER to overwrite +# the default one, e.g., +# INSTRUMENTED_PACKAGE_RUNNER=com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner \ +# ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py +# def main(): build_top = os.environ.get('ANDROID_BUILD_TOP', None) out_dir = os.environ.get('OUT', None) + runner = os.environ.get('INSTRUMENTED_PACKAGE_RUNNER', None) if build_top is None or out_dir is None: print 'You need to source and lunch before you can use this script' return 1 - + if runner is None: + runner = INSTRUMENTED_PACKAGE_RUNNER print 'Building tests...' run('make -j32 -C %s -f build/core/main.mk ' 'MODULES-IN-frameworks-base-services-tests-servicestests' % build_top, @@ -57,19 +69,19 @@ def main(): apk_path = ( '%s/data/app/FrameworksServicesTests/FrameworksServicesTests.apk' % out_dir) - run('adb install -r -g "%s"' % apk_path) + run('adb install -t -r -g "%s"' % apk_path) print 'Running tests...' if len(sys.argv) != 1: run('adb shell am instrument -w %s "%s"' % - (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER)) + (' '.join(sys.argv[1:]), runner)) return 0 # It would be nice if the activity manager accepted a list of packages, but # in lieu of that... for package in PACKAGE_WHITELIST: run('adb shell am instrument -w -e package %s %s' % - (package, INSTRUMENTED_PACKAGE_RUNNER)) + (package, runner)) return 0 diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index bf7836d42008..00ebae6613cc 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -57,6 +57,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libbacktrace \ libbase \ libbinder \ + libbinderthreadstate \ libc++ \ libcutils \ liblog \ diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk index b98bc8937aa9..3fa776cc3180 100644 --- a/services/tests/uiservicestests/Android.mk +++ b/services/tests/uiservicestests/Android.mk @@ -45,6 +45,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libbacktrace \ libbase \ libbinder \ + libbinderthreadstate \ libc++ \ libcutils \ liblog \ diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp new file mode 100644 index 000000000000..b3b09001e7ee --- /dev/null +++ b/startop/iorap/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_library_static { + name: "libiorap-java", + + aidl: { + include_dirs: [ + "system/iorap/binder", + ], + }, + + srcs: [ + ":iorap-aidl", + "**/*.java", + ], +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java new file mode 100644 index 000000000000..1d38f4c1e23d --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.os.Parcelable; +import android.os.Parcel; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Provide a hint to iorapd that an activity has transitioned state.<br /><br /> + * + * Knowledge of when an activity starts/stops can be used by iorapd to increase system + * performance (e.g. by launching perfetto tracing to record an io profile, or by + * playing back an ioprofile via readahead) over the long run.<br /><br /> + * + * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br /> + * + * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type. + * All other states could be terminal, see below: <br /><br /> + * + * <pre> + * + * ┌──────────────────────────────────────┐ + * │ ▼ + * ┌─────────┐ ╔════════════════╗ ╔═══════════╗ + * ──▶ │ STARTED │ ──▶ ║ COMPLETED ║ ──▶ ║ CANCELLED ║ + * └─────────┘ ╚════════════════╝ ╚═══════════╝ + * │ + * │ + * ▼ + * ╔════════════════╗ + * ║ POST_COMPLETED ║ + * ╚════════════════╝ + * + * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot --> + * + * @hide + */ +public class ActivityHintEvent implements Parcelable { + + public static final int TYPE_STARTED = 0; + public static final int TYPE_CANCELLED = 1; + public static final int TYPE_COMPLETED = 2; + public static final int TYPE_POST_COMPLETED = 3; + private static final int TYPE_MAX = TYPE_POST_COMPLETED; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_STARTED, + TYPE_CANCELLED, + TYPE_COMPLETED, + TYPE_POST_COMPLETED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + @Type public final int type; + public final ActivityInfo activityInfo; + + public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) { + this.type = type; + this.activityInfo = activityInfo; + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + CheckHelpers.checkTypeInRange(type, TYPE_MAX); + Objects.requireNonNull(activityInfo, "activityInfo"); + } + + @Override + public String toString() { + return String.format("{type: %d, activityInfo: %s}", type, activityInfo); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof ActivityHintEvent) { + return equals((ActivityHintEvent) other); + } + return false; + } + + private boolean equals(ActivityHintEvent other) { + return type == other.type && + Objects.equals(activityInfo, other.activityInfo); + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(type); + activityInfo.writeToParcel(out, flags); + } + + private ActivityHintEvent(Parcel in) { + this.type = in.readInt(); + this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in); + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<ActivityHintEvent> CREATOR + = new Parcelable.Creator<ActivityHintEvent>() { + public ActivityHintEvent createFromParcel(Parcel in) { + return new ActivityHintEvent(in); + } + + public ActivityHintEvent[] newArray(int size) { + return new ActivityHintEvent[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java new file mode 100644 index 000000000000..f47a42cffdd8 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import java.util.Objects; + +import android.os.Parcelable; +import android.os.Parcel; + +/** + * Provide minimal information for launched activities to iorap.<br /><br /> + * + * This uniquely identifies a system-wide activity by providing the {@link #packageName} and + * {@link #activityName}. + * + * @see ActivityHintEvent + * @see AppIntentEvent + * + * @hide + */ +public class ActivityInfo implements Parcelable { + + /** The name of the package, for example {@code com.android.calculator}. */ + public final String packageName; + /** The name of the activity, for example {@code .activities.activity.MainActivity} */ + public final String activityName; + + public ActivityInfo(String packageName, String activityName) { + this.packageName = packageName; + this.activityName = activityName; + + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + Objects.requireNonNull(packageName, "packageName"); + Objects.requireNonNull(activityName, "activityName"); + } + + @Override + public String toString() { + return String.format("{packageName: %s, activityName: %s}", packageName, activityName); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof ActivityInfo) { + return equals((ActivityInfo) other); + } + return false; + } + + private boolean equals(ActivityInfo other) { + return Objects.equals(packageName, other.packageName) && + Objects.equals(activityName, other.activityName); + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(packageName); + out.writeString(activityName); + } + + private ActivityInfo(Parcel in) { + packageName = in.readString(); + activityName = in.readString(); + + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<ActivityInfo> CREATOR + = new Parcelable.Creator<ActivityInfo>() { + public ActivityInfo createFromParcel(Parcel in) { + return new ActivityInfo(in); + } + + public ActivityInfo[] newArray(int size) { + return new ActivityInfo[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java new file mode 100644 index 000000000000..1cd37b5546b9 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.os.Parcelable; +import android.os.Parcel; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br /> + * + * Intent defaults provide a mechanism for an app to register itself as an automatic handler. + * For example the camera app might be registered as the default handler for + * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently, + * if an arbitrary other app requests for a still image camera photo to be taken, the system + * will launch the respective default camera app to be launched to handle that request.<br /><br /> + * + * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of + * applications that resolve from the default intent. If the application would now be resolved + * differently, iorapd would unpin the old application and pin the new application.<br /><br /> + * + * @hide + */ +public class AppIntentEvent implements Parcelable { + + /** @see android.content.Intent#CATEGORY_DEFAULT */ + public static final int TYPE_DEFAULT_INTENT_CHANGED = 0; + private static final int TYPE_MAX = 0; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_DEFAULT_INTENT_CHANGED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + @Type public final int type; + + public final ActivityInfo oldActivityInfo; + public final ActivityInfo newActivityInfo; + + // TODO: Probably need the corresponding action here as well. + + public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo, + ActivityInfo newActivityInfo) { + return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo, + newActivityInfo); + } + + private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo, + ActivityInfo newActivityInfo) { + this.type = type; + this.oldActivityInfo = oldActivityInfo; + this.newActivityInfo = newActivityInfo; + + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + CheckHelpers.checkTypeInRange(type, TYPE_MAX); + Objects.requireNonNull(oldActivityInfo, "oldActivityInfo"); + Objects.requireNonNull(oldActivityInfo, "newActivityInfo"); + } + + @Override + public String toString() { + return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo, + newActivityInfo); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof AppIntentEvent) { + return equals((AppIntentEvent) other); + } + return false; + } + + private boolean equals(AppIntentEvent other) { + return type == other.type && + Objects.equals(oldActivityInfo, other.oldActivityInfo) && + Objects.equals(newActivityInfo, other.newActivityInfo); + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(type); + oldActivityInfo.writeToParcel(out, flags); + newActivityInfo.writeToParcel(out, flags); + } + + private AppIntentEvent(Parcel in) { + this.type = in.readInt(); + this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in); + this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in); + + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<AppIntentEvent> CREATOR + = new Parcelable.Creator<AppIntentEvent>() { + public AppIntentEvent createFromParcel(Parcel in) { + return new AppIntentEvent(in); + } + + public AppIntentEvent[] newArray(int size) { + return new AppIntentEvent[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java new file mode 100644 index 000000000000..34aedd7685d8 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.startop.iorap; + +/** + * Convenience short-hand to throw {@link IllegalAccessException} when the arguments + * are out-of-range. + */ +public class CheckHelpers { + /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */ + public static void checkTypeInRange(int type, int maxValue) { + if (type < 0) { + throw new IllegalArgumentException( + String.format("type must be non-negative (value=%d)", type)); + } + if (type > maxValue) { + throw new IllegalArgumentException( + String.format("type out of range (value=%d, max=%d)", type, maxValue)); + } + } + + /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */ + public static void checkStateInRange(int state, int maxValue) { + if (state < 0) { + throw new IllegalArgumentException( + String.format("state must be non-negative (value=%d)", state)); + } + if (state > maxValue) { + throw new IllegalArgumentException( + String.format("state out of range (value=%d, max=%d)", state, maxValue)); + } + } +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java new file mode 100644 index 000000000000..aa4eea716363 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.annotation.NonNull; +import android.os.Parcelable; +import android.os.Parcel; +import android.net.Uri; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Forward package manager events to iorapd. <br /><br /> + * + * Knowing when packages are modified by the system are a useful tidbit to help with performance: + * for example when a package is replaced, it could be a hint used to invalidate any collected + * io profiles used for prefetching or pinning. + * + * @hide + */ +public class PackageEvent implements Parcelable { + + /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */ + public static final int TYPE_REPLACED = 0; + private static final int TYPE_MAX = 0; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_REPLACED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + @Type public final int type; + + /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */ + public final Uri packageUri; + /** The name of the package, for example {@code com.android.calculator}. */ + public final String packageName; + + @NonNull + public static PackageEvent createReplaced(Uri packageUri, String packageName) { + return new PackageEvent(TYPE_REPLACED, packageUri, packageName); + } + + private PackageEvent(@Type int type, Uri packageUri, String packageName) { + this.type = type; + this.packageUri = packageUri; + this.packageName = packageName; + + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + CheckHelpers.checkTypeInRange(type, TYPE_MAX); + Objects.requireNonNull(packageUri, "packageUri"); + Objects.requireNonNull(packageName, "packageName"); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof PackageEvent) { + return equals((PackageEvent) other); + } + return false; + } + + private boolean equals(PackageEvent other) { + return type == other.type && + Objects.equals(packageUri, other.packageUri) && + Objects.equals(packageName, other.packageName); + } + + @Override + public String toString() { + return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName); + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(type); + packageUri.writeToParcel(out, flags); + out.writeString(packageName); + } + + private PackageEvent(Parcel in) { + this.type = in.readInt(); + this.packageUri = Uri.CREATOR.createFromParcel(in); + this.packageName = in.readString(); + + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<PackageEvent> CREATOR + = new Parcelable.Creator<PackageEvent>() { + public PackageEvent createFromParcel(Parcel in) { + return new PackageEvent(in); + } + + public PackageEvent[] newArray(int size) { + return new PackageEvent[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java new file mode 100644 index 000000000000..2c79319a1459 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.os.Parcelable; +import android.os.Parcel; + +import android.annotation.NonNull; + +/** + * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation, + * used for asynchronous callbacks by the server. <br /><br /> + * + * As all system server binder calls must be {@code oneway}, this means all invocations + * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID + * exists to associate all calls with their respective callbacks in + * {@link com.google.android.startop.iorap.ITaskListener}. + * + * @see com.google.android.startop.iorap.IIorap + * + * @hide + */ +public class RequestId implements Parcelable { + + public final long requestId; + + private static Object mLock = new Object(); + private static long mNextRequestId = 0; + + /** + * Create a monotonically increasing request ID.<br /><br /> + * + * It is invalid to re-use the same request ID for multiple method calls on + * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created + * each time. + */ + @NonNull public static RequestId nextValueForSequence() { + long currentRequestId; + synchronized (mLock) { + currentRequestId = mNextRequestId; + ++mNextRequestId; + } + return new RequestId(currentRequestId); + } + + private RequestId(long requestId) { + this.requestId = requestId; + + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + if (requestId < 0) { + throw new IllegalArgumentException("request id must be non-negative"); + } + } + + @Override + public String toString() { + return String.format("{requestId: %ld}", requestId); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof RequestId) { + return equals((RequestId) other); + } + return false; + } + + private boolean equals(RequestId other) { + return requestId == other.requestId; + } + + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(requestId); + } + + private RequestId(Parcel in) { + requestId = in.readLong(); + + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<RequestId> CREATOR + = new Parcelable.Creator<RequestId>() { + public RequestId createFromParcel(Parcel in) { + return new RequestId(in); + } + + public RequestId[] newArray(int size) { + return new RequestId[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java new file mode 100644 index 000000000000..75d47f9e3d17 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.os.Parcelable; +import android.os.Parcel; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Forward system service events to iorapd. + * + * @see com.android.server.SystemService + * + * @hide + */ +public class SystemServiceEvent implements Parcelable { + + /** @see com.android.server.SystemService#onBootPhase */ + public static final int TYPE_BOOT_PHASE = 0; + /** @see com.android.server.SystemService#onStart */ + public static final int TYPE_START = 1; + private static final int TYPE_MAX = TYPE_START; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_BOOT_PHASE, + TYPE_START, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + @Type public final int type; + + // TODO: do we want to pass the exact build phase enum? + + public SystemServiceEvent(@Type int type) { + this.type = type; + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + CheckHelpers.checkTypeInRange(type, TYPE_MAX); + } + + @Override + public String toString() { + return String.format("{type: %d}", type); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof SystemServiceEvent) { + return equals((SystemServiceEvent) other); + } + return false; + } + + private boolean equals(SystemServiceEvent other) { + return type == other.type; + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(type); + } + + private SystemServiceEvent(Parcel in) { + this.type = in.readInt(); + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<SystemServiceEvent> CREATOR + = new Parcelable.Creator<SystemServiceEvent>() { + public SystemServiceEvent createFromParcel(Parcel in) { + return new SystemServiceEvent(in); + } + + public SystemServiceEvent[] newArray(int size) { + return new SystemServiceEvent[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java new file mode 100644 index 000000000000..b77c03c1584a --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.os.Parcelable; +import android.os.Parcel; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Forward user events to iorapd.<br /><br /> + * + * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies + * by iorapd (e.g. to handle user default pinned applications changing). + * + * @see com.android.server.SystemService + * + * @hide + */ +public class SystemServiceUserEvent implements Parcelable { + + /** @see com.android.server.SystemService#onStartUser */ + public static final int TYPE_START_USER = 0; + /** @see com.android.server.SystemService#onUnlockUser */ + public static final int TYPE_UNLOCK_USER = 1; + /** @see com.android.server.SystemService#onSwitchUser*/ + public static final int TYPE_SWITCH_USER = 2; + /** @see com.android.server.SystemService#onStopUser */ + public static final int TYPE_STOP_USER = 3; + /** @see com.android.server.SystemService#onCleanupUser */ + public static final int TYPE_CLEANUP_USER = 4; + private static final int TYPE_MAX = TYPE_CLEANUP_USER; + + /** @hide */ + @IntDef(flag = true, prefix = { "TYPE_" }, value = { + TYPE_START_USER, + TYPE_UNLOCK_USER, + TYPE_SWITCH_USER, + TYPE_STOP_USER, + TYPE_CLEANUP_USER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + @Type public final int type; + public final int userHandle; + + public SystemServiceUserEvent(@Type int type, int userHandle) { + this.type = type; + this.userHandle = userHandle; + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + CheckHelpers.checkTypeInRange(type, TYPE_MAX); + if (userHandle < 0) { + throw new IllegalArgumentException("userHandle must be non-negative"); + } + } + + @Override + public String toString() { + return String.format("{type: %d, userHandle: %d}", type, userHandle); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof SystemServiceUserEvent) { + return equals((SystemServiceUserEvent) other); + } + return false; + } + + private boolean equals(SystemServiceUserEvent other) { + return type == other.type && + userHandle == other.userHandle; + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(type); + out.writeInt(userHandle); + } + + private SystemServiceUserEvent(Parcel in) { + this.type = in.readInt(); + this.userHandle = in.readInt(); + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR + = new Parcelable.Creator<SystemServiceUserEvent>() { + public SystemServiceUserEvent createFromParcel(Parcel in) { + return new SystemServiceUserEvent(in); + } + + public SystemServiceUserEvent[] newArray(int size) { + return new SystemServiceUserEvent[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java new file mode 100644 index 000000000000..b5fd6d8d1c45 --- /dev/null +++ b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.startop.iorap; + +import android.os.Parcelable; +import android.os.Parcel; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener} + * callbacks.<br /><br /> + * + * Following {@link com.google.android.startop.iorap.IIorap} method invocation, + * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br /> + * + * State transitions are as follows: <br /><br /> + * + * <pre> + * ┌─────────────────────────────┐ + * │ ▼ + * ┌───────┐ ┌─────────┐ ╔═══════════╗ + * ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║ + * └───────┘ └─────────┘ ╚═══════════╝ + * │ │ + * │ │ + * ▼ │ + * ╔═══════╗ │ + * ──▶ ║ ERROR ║ ◀─────┘ + * ╚═══════╝ + * + * </pre> <!-- system/iorap/docs/binder/TaskResult.dot --> + * + * @hide + */ +public class TaskResult implements Parcelable { + + public static final int STATE_BEGAN = 0; + public static final int STATE_ONGOING = 1; + public static final int STATE_COMPLETED = 2; + public static final int STATE_ERROR = 3; + private static final int STATE_MAX = STATE_ERROR; + + /** @hide */ + @IntDef(flag = true, prefix = { "STATE_" }, value = { + STATE_BEGAN, + STATE_ONGOING, + STATE_COMPLETED, + STATE_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State {} + + @State public final int state; + + @Override + public String toString() { + return String.format("{state: %d}", state); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof TaskResult) { + return equals((TaskResult) other); + } + return false; + } + + private boolean equals(TaskResult other) { + return state == other.state; + } + + public TaskResult(@State int state) { + this.state = state; + + checkConstructorArguments(); + } + + private void checkConstructorArguments() { + CheckHelpers.checkStateInRange(state, STATE_MAX); + } + + //<editor-fold desc="Binder boilerplate"> + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(state); + } + + private TaskResult(Parcel in) { + state = in.readInt(); + + checkConstructorArguments(); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<TaskResult> CREATOR + = new Parcelable.Creator<TaskResult>() { + public TaskResult createFromParcel(Parcel in) { + return new TaskResult(in); + } + + public TaskResult[] newArray(int size) { + return new TaskResult[size]; + } + }; + //</editor-fold> +} diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp new file mode 100644 index 000000000000..76057846e896 --- /dev/null +++ b/startop/iorap/tests/Android.bp @@ -0,0 +1,40 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO: once b/80095087 is fixed, rewrite this back to android_test +java_library { + name: "libiorap-java-test-lib", + srcs: ["src/**/*.kt"], + + static_libs: [ + // non-test dependencies + "libiorap-java", + // test android dependencies + "platform-test-annotations", + "android-support-test", + // test framework dependencies + "mockito-target-inline-minus-junit4", + // "mockito-target-minus-junit4", + // Mockito also requires JNI (see Android.mk) + // and android:debuggable=true (see AndroidManifest.xml) + "truth-prebuilt", + ], + + // sdk_version: "current", + // certificate: "platform", + + libs: ["android.test.base", "android.test.runner"], + + // test_suites: ["device-tests"], +} diff --git a/startop/iorap/tests/Android.mk b/startop/iorap/tests/Android.mk new file mode 100644 index 000000000000..1b2aa46a6418 --- /dev/null +++ b/startop/iorap/tests/Android.mk @@ -0,0 +1,46 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# android_test does not support JNI libraries +# TODO: once b/80095087 is fixed, rewrite this back to android_test +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_JACK_FLAGS := --multi-dex native +LOCAL_DX_FLAGS := --multi-dex + +LOCAL_PACKAGE_NAME := libiorap-java-tests +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_STATIC_JAVA_LIBRARIES := \ + libiorap-java-test-lib + +LOCAL_MULTILIB := both + +LOCAL_JNI_SHARED_LIBRARIES := \ + libdexmakerjvmtiagent \ + libstaticjvmtiagent \ + libmultiplejvmtiagentsinterferenceagent + +LOCAL_JAVA_LIBRARIES := \ + android.test.base \ + android.test.runner + +# Use private APIs +LOCAL_CERTIFICATE := platform +LOCAL_PRIVATE_PLATFORM_APIS := true + +include $(BUILD_PACKAGE) diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml new file mode 100644 index 000000000000..99f4add6579f --- /dev/null +++ b/startop/iorap/tests/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!--suppress AndroidUnknownAttribute --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.startop.iorap.tests" + android:sharedUserId="com.google.android.startop.iorap.tests" + android:versionCode="1" + android:versionName="1.0" > + + <!--suppress AndroidDomInspection --> + <instrumentation + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.google.android.startop.iorap.tests" /> + + <!-- + 'debuggable=true' is required to properly load mockito jvmti dependencies, + otherwise it gives the following error at runtime: + + Openjdkjvmti plugin was loaded on a non-debuggable Runtime. + Plugin was loaded too late to change runtime state to DEBUGGABLE. --> + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> +</manifest> diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt new file mode 100644 index 000000000000..4ba44a93f2a8 --- /dev/null +++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.android.startop.iorap + +import android.net.Uri +import android.os.ServiceManager +import android.support.test.filters.MediumTest +import org.junit.Test +import org.junit.Ignore +import org.mockito.Mockito.* + +// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it") +@MediumTest +class IIorapIntegrationTest { + /** + * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found + */ + private val iorapService : IIorap by lazy { + // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply. + IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd")) + + // Use 'adb shell setenforce 0' otherwise this whole test fails, + // because the servicemanager is not allowed to hand out the binder token for iorapd. + + // TODO: implement the selinux policies for iorapd. + } + + // A dummy binder stub implementation is required to use with mockito#spy. + // Mockito overrides the methods at runtime and tracks how methods were invoked. + open class DummyTaskListener : ITaskListener.Stub() { + // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions + // from using the mockito matchers (eq, argThat, etc). + override fun onProgress(requestId: RequestId?, result: TaskResult?) { + } + + override fun onComplete(requestId: RequestId?, result: TaskResult?) { + } + } + + private fun testAnyMethod(func : (RequestId) -> Unit) { + val taskListener = spy(DummyTaskListener())!! + + try { + iorapService.setTaskListener(taskListener) + // Note: Binder guarantees total order for oneway messages sent to the same binder + // interface, so we don't need any additional blocking here before sending later calls. + + // Every new method call should have a unique request id. + val requestId = RequestId.nextValueForSequence()!! + + // Apply the specific function under test. + func(requestId) + + // Typical mockito behavior is to allow any-order callbacks, but we want to test order. + val inOrder = inOrder(taskListener) + + // The "stub" behavior of iorapd is that every request immediately gets a response of + // BEGAN,ONGOING,COMPLETED + inOrder.verify(taskListener, timeout(100)). + onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN }) + inOrder.verify(taskListener, timeout(100)). + onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING }) + inOrder.verify(taskListener, timeout(100)). + onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED }) + inOrder.verifyNoMoreInteractions() + + } finally { + iorapService.setTaskListener(null) + } + } + + @Test + fun testOnPackageEvent() { + testAnyMethod { requestId : RequestId -> + iorapService.onPackageEvent(requestId, + PackageEvent.createReplaced( + Uri.parse("https://www.google.com"), "com.fake.package")) + } + } + + @Test + fun testOnAppIntentEvent() { + testAnyMethod { requestId : RequestId -> + iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged( + ActivityInfo("dont care", "dont care"), + ActivityInfo("dont care 2", "dont care 2"))) + } + } + + @Test + fun testOnSystemServiceEvent() { + testAnyMethod { requestId : RequestId -> + iorapService.onSystemServiceEvent(requestId, + SystemServiceEvent(SystemServiceEvent.TYPE_START)) + } + } + + @Test + fun testOnSystemServiceUserEvent() { + testAnyMethod { requestId : RequestId -> + iorapService.onSystemServiceUserEvent(requestId, + SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER,0)) + } + } +} diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt new file mode 100644 index 000000000000..4abbb3e9f162 --- /dev/null +++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.google.android.startop.iorap + +import android.net.Uri +import android.os.Parcel +import android.os.Parcelable +import android.support.test.filters.SmallTest +import org.junit.Test +import org.junit.runner.RunWith +import com.google.common.truth.Truth.assertThat +import org.junit.runners.Parameterized + +/** + * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap] + * have a valid-conforming interface implementation. + */ +@SmallTest +@RunWith(Parameterized::class) +class ParcelablesTest<T : Parcelable>(private val inputData : InputData<T>) { + companion object { + private val initialRequestId = RequestId.nextValueForSequence()!! + + @JvmStatic + @Parameterized.Parameters + fun data() = listOf( + InputData( + newActivityInfo(), + newActivityInfo(), + ActivityInfo("some package", "some other activity")), + InputData( + ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()), + ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()), + ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED, + newActivityInfo())), + InputData( + AppIntentEvent.createDefaultIntentChanged(newActivityInfo(), + newActivityInfoOther()), + AppIntentEvent.createDefaultIntentChanged(newActivityInfo(), + newActivityInfoOther()), + AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(), + newActivityInfo())), + InputData( + PackageEvent.createReplaced(newUri(), "some package"), + PackageEvent.createReplaced(newUri(), "some package"), + PackageEvent.createReplaced(newUri(), "some other package") + ), + InputData(initialRequestId, cloneRequestId(initialRequestId), + RequestId.nextValueForSequence()), + InputData( + SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE), + SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE), + SystemServiceEvent(SystemServiceEvent.TYPE_START)), + InputData( + SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345), + SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345), + SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)), + InputData( + TaskResult(TaskResult.STATE_COMPLETED), + TaskResult(TaskResult.STATE_COMPLETED), + TaskResult(TaskResult.STATE_ONGOING)) + ) + + private fun newActivityInfo() : ActivityInfo { + return ActivityInfo("some package", "some activity") + } + + private fun newActivityInfoOther() : ActivityInfo { + return ActivityInfo("some package 2", "some activity 2") + } + + private fun newUri() : Uri { + return Uri.parse("https://www.google.com") + } + + private fun cloneRequestId(requestId: RequestId) : RequestId { + val constructor = requestId::class.java.declaredConstructors[0] + constructor.isAccessible = true + return constructor.newInstance(requestId.requestId) as RequestId + } + } + + /** + * Test for [Object.equals] implementation. + */ + @Test + fun testEquality() { + assertThat(inputData.valid).isEqualTo(inputData.valid) + assertThat(inputData.valid).isEqualTo(inputData.validCopy) + assertThat(inputData.valid).isNotEqualTo(inputData.validOther) + } + + /** + * Test for [Parcelable] implementation. + */ + @Test + fun testParcelRoundTrip() { + // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data. + val assertParcels = { it : T, data : InputData<T> -> + val parcel = Parcel.obtain() + it.writeToParcel(parcel, 0) + parcel.setDataPosition(0) // future reads will see all previous writes. + assertThat(it).isEqualTo(data.createFromParcel(parcel)) + parcel.recycle() + } + + assertParcels(inputData.valid, inputData) + assertParcels(inputData.validCopy, inputData) + assertParcels(inputData.validOther, inputData) + } + + data class InputData<T : Parcelable>(val valid : T, val validCopy : T, val validOther : T) { + val kls = valid.javaClass + init { + assertThat(valid).isNotSameAs(validCopy) + // Don't use isInstanceOf because of phantom warnings in intellij about Class! + assertThat(validCopy.javaClass).isEqualTo(valid.javaClass) + assertThat(validOther.javaClass).isEqualTo(valid.javaClass) + } + + fun createFromParcel(parcel : Parcel) : T { + val field = kls.getDeclaredField("CREATOR") + val creator = field.get(null) as Parcelable.Creator<T> + + return creator.createFromParcel(parcel) + } + } +} diff --git a/startop/tools/view_compiler/Android.bp b/startop/tools/view_compiler/Android.bp new file mode 100644 index 000000000000..c3e91849e636 --- /dev/null +++ b/startop/tools/view_compiler/Android.bp @@ -0,0 +1,49 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library_host_static { + name: "libviewcompiler", + srcs: [ + "java_lang_builder.cc", + "util.cc", + ], + static_libs: [ + "libbase" + ] +} + +cc_binary_host { + name: "viewcompiler", + srcs: [ + "main.cc", + ], + static_libs: [ + "libbase", + "libtinyxml2", + "libgflags", + "libviewcompiler", + ], +} + +cc_test_host { + name: "view-compiler-tests", + srcs: [ + "util_test.cc", + ], + static_libs: [ + "libviewcompiler", + ] +} diff --git a/startop/tools/view_compiler/README.md b/startop/tools/view_compiler/README.md new file mode 100644 index 000000000000..56595016cbb9 --- /dev/null +++ b/startop/tools/view_compiler/README.md @@ -0,0 +1,25 @@ +# View Compiler + +This directory contains an experimental compiler for layout files. + +It will take a layout XML file and produce a CompiledLayout.java file with a +specialized layout inflation function. + +To use it, let's assume you had a layout in `my_layout.xml` and your app was in +the Java language package `com.example.myapp`. Run the following command: + + viewcompiler my_layout.xml --package com.example.myapp --out CompiledView.java + +This will produce a `CompiledView.java`, which can then be compiled into your +Android app. Then to use it, in places where you would have inflated +`R.layouts.my_layout`, instead call `CompiledView.inflate`. + +Precompiling views like this generally improves the time needed to inflate them. + +This tool is still in its early stages and has a number of limitations. +* Currently only one layout can be compiled at a time. +* `merge` and `include` nodes are not supported. +* View compilation is a manual process that requires code changes in the + application. +* This only works for apps that do not use a custom layout inflater. +* Other limitations yet to be discovered. diff --git a/startop/tools/view_compiler/TEST_MAPPING b/startop/tools/view_compiler/TEST_MAPPING new file mode 100644 index 000000000000..cc4b17a7a65a --- /dev/null +++ b/startop/tools/view_compiler/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "view-compiler-tests" + } + ] +} diff --git a/startop/tools/view_compiler/java_lang_builder.cc b/startop/tools/view_compiler/java_lang_builder.cc new file mode 100644 index 000000000000..0b8754fc7096 --- /dev/null +++ b/startop/tools/view_compiler/java_lang_builder.cc @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "java_lang_builder.h" + +#include "android-base/stringprintf.h" + +using android::base::StringPrintf; +using std::string; + +void JavaLangViewBuilder::Start() const { + out_ << StringPrintf("package %s;\n", package_.c_str()) + << "import android.content.Context;\n" + "import android.content.res.Resources;\n" + "import android.content.res.XmlResourceParser;\n" + "import android.util.AttributeSet;\n" + "import android.util.Xml;\n" + "import android.view.*;\n" + "import android.widget.*;\n" + "\n" + "public final class CompiledView {\n" + "\n" + "static <T extends View> T createView(Context context, AttributeSet attrs, View parent, " + "String name, LayoutInflater.Factory factory, LayoutInflater.Factory2 factory2) {" + "\n" + " if (factory2 != null) {\n" + " return (T)factory2.onCreateView(parent, name, context, attrs);\n" + " } else if (factory != null) {\n" + " return (T)factory.onCreateView(name, context, attrs);\n" + " }\n" + // TODO: find a way to call the private factory + " return null;\n" + "}\n" + "\n" + " public static View inflate(Context context) {\n" + " try {\n" + " LayoutInflater inflater = LayoutInflater.from(context);\n" + " LayoutInflater.Factory factory = inflater.getFactory();\n" + " LayoutInflater.Factory2 factory2 = inflater.getFactory2();\n" + " Resources res = context.getResources();\n" + << StringPrintf(" XmlResourceParser xml = res.getLayout(%s.R.layout.%s);\n", + package_.c_str(), + layout_name_.c_str()) + << " AttributeSet attrs = Xml.asAttributeSet(xml);\n" + // The Java-language XmlPullParser needs a call to next to find the start document tag. + " xml.next(); // start document\n"; +} + +void JavaLangViewBuilder::Finish() const { + out_ << " } catch (Exception e) {\n" + " return null;\n" + " }\n" // end try + " }\n" // end inflate + "}\n"; // end CompiledView +} + +void JavaLangViewBuilder::StartView(const string& class_name) { + const string view_var = MakeVar("view"); + const string layout_var = MakeVar("layout"); + std::string parent = "null"; + if (!view_stack_.empty()) { + const StackEntry& parent_entry = view_stack_.back(); + parent = parent_entry.view_var; + } + out_ << " xml.next(); // <" << class_name << ">\n" + << StringPrintf(" %s %s = createView(context, attrs, %s, \"%s\", factory, factory2);\n", + class_name.c_str(), + view_var.c_str(), + parent.c_str(), + class_name.c_str()) + << StringPrintf(" if (%s == null) %s = new %s(context, attrs);\n", + view_var.c_str(), + view_var.c_str(), + class_name.c_str()); + if (!view_stack_.empty()) { + out_ << StringPrintf(" ViewGroup.LayoutParams %s = %s.generateLayoutParams(attrs);\n", + layout_var.c_str(), + parent.c_str()); + } + view_stack_.push_back({class_name, view_var, layout_var}); +} + +void JavaLangViewBuilder::FinishView() { + const StackEntry var = view_stack_.back(); + view_stack_.pop_back(); + if (!view_stack_.empty()) { + const string& parent = view_stack_.back().view_var; + out_ << StringPrintf(" xml.next(); // </%s>\n", var.class_name.c_str()) + << StringPrintf(" %s.addView(%s, %s);\n", + parent.c_str(), + var.view_var.c_str(), + var.layout_params_var.c_str()); + } else { + out_ << StringPrintf(" return %s;\n", var.view_var.c_str()); + } +} + +const std::string JavaLangViewBuilder::MakeVar(std::string prefix) { + std::stringstream v; + v << prefix << view_id_++; + return v.str(); +} diff --git a/startop/tools/view_compiler/java_lang_builder.h b/startop/tools/view_compiler/java_lang_builder.h new file mode 100644 index 000000000000..c8d20b23cd13 --- /dev/null +++ b/startop/tools/view_compiler/java_lang_builder.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef JAVA_LANG_BUILDER_H_ +#define JAVA_LANG_BUILDER_H_ + +#include <iostream> +#include <sstream> +#include <vector> + +// Build Java language code to instantiate views. +// +// This has a very small interface to make it easier to generate additional +// backends, such as a direct-to-DEX version. +class JavaLangViewBuilder { + public: + JavaLangViewBuilder(std::string package, std::string layout_name, std::ostream& out = std::cout) + : package_(package), layout_name_(layout_name), out_(out) {} + + // Begin generating a class. Adds the package boilerplate, etc. + void Start() const; + // Finish generating a class, closing off any open curly braces, etc. + void Finish() const; + + // Begin creating a view (i.e. process the opening tag) + void StartView(const std::string& class_name); + // Finish a view, after all of its child nodes have been processed. + void FinishView(); + + private: + const std::string MakeVar(std::string prefix); + + std::string const package_; + std::string const layout_name_; + + std::ostream& out_; + + size_t view_id_ = 0; + + struct StackEntry { + // The class name for this view object + const std::string class_name; + + // The variable name that is holding the view object + const std::string view_var; + + // The variable name that holds the object's layout parameters + const std::string layout_params_var; + }; + std::vector<StackEntry> view_stack_; +}; + +#endif // JAVA_LANG_BUILDER_H_ diff --git a/startop/tools/view_compiler/main.cc b/startop/tools/view_compiler/main.cc new file mode 100644 index 000000000000..0ad7e24feb3b --- /dev/null +++ b/startop/tools/view_compiler/main.cc @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gflags/gflags.h" + +#include "java_lang_builder.h" +#include "util.h" + +#include "tinyxml2.h" + +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +using namespace tinyxml2; +using std::string; + +constexpr char kStdoutFilename[]{"stdout"}; + +DEFINE_string(package, "", "The package name for the generated class (required)"); +DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); + +namespace { +class ViewCompilerXmlVisitor : public XMLVisitor { + public: + ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {} + + bool VisitEnter(const XMLDocument& /*doc*/) override { + builder_->Start(); + return true; + } + + bool VisitExit(const XMLDocument& /*doc*/) override { + builder_->Finish(); + return true; + } + + bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override { + builder_->StartView(element.Name()); + return true; + } + + bool VisitExit(const XMLElement& /*element*/) override { + builder_->FinishView(); + return true; + } + + private: + JavaLangViewBuilder* builder_; +}; +} // end namespace + +int main(int argc, char** argv) { + constexpr size_t kProgramName = 0; + constexpr size_t kFileNameParam = 1; + constexpr size_t kNumRequiredArgs = 2; + + gflags::SetUsageMessage( + "Compile XML layout files into equivalent Java language code\n" + "\n" + " example usage: viewcompiler layout.xml --package com.example.androidapp"); + gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); + + gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); + if (argc != kNumRequiredArgs || cmd.is_default) { + gflags::ShowUsageWithFlags(argv[kProgramName]); + return 1; + } + + const char* const filename = argv[kFileNameParam]; + const string layout_name = FindLayoutNameFromFilename(filename); + + // We want to generate Java language code to inflate exactly this layout. This means + // generating code to walk the resource XML too. + + XMLDocument xml; + xml.LoadFile(filename); + + std::ofstream outfile; + if (FLAGS_out != kStdoutFilename) { + outfile.open(FLAGS_out); + } + JavaLangViewBuilder builder{ + FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile}; + + ViewCompilerXmlVisitor visitor{&builder}; + xml.Accept(&visitor); + + return 0; +}
\ No newline at end of file diff --git a/startop/tools/view_compiler/util.cc b/startop/tools/view_compiler/util.cc new file mode 100644 index 000000000000..69df41dff3d7 --- /dev/null +++ b/startop/tools/view_compiler/util.cc @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util.h" + +using std::string; + +// TODO: see if we can borrow this from somewhere else, like aapt2. +string FindLayoutNameFromFilename(const string& filename) { + size_t start = filename.rfind("/"); + if (start == string::npos) { + start = 0; + } else { + start++; // advance past '/' character + } + size_t end = filename.find(".", start); + + return filename.substr(start, end - start); +} diff --git a/startop/tools/view_compiler/util.h b/startop/tools/view_compiler/util.h new file mode 100644 index 000000000000..03e093920bfa --- /dev/null +++ b/startop/tools/view_compiler/util.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include <string> + +std::string FindLayoutNameFromFilename(const std::string& filename); + +#endif // UTIL_H_ diff --git a/startop/tools/view_compiler/util_test.cc b/startop/tools/view_compiler/util_test.cc new file mode 100644 index 000000000000..d1540d3a6e43 --- /dev/null +++ b/startop/tools/view_compiler/util_test.cc @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util.h" + +#include "gtest/gtest.h" + +using std::string; + +TEST(UtilTest, FindLayoutNameFromFilename) { + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("foo/bar.xml")); + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("bar.xml")); + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("./foo/bar.xml")); + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("/foo/bar.xml")); +} diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 8c37a21afa50..d33a537f2194 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1905,6 +1905,22 @@ public class TelecomManager { return false; } + /** + * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity. + * @param intent The {@link Intent#ACTION_CALL} intent to handle. + * @hide + */ + public void handleCallIntent(Intent intent) { + try { + if (isServiceConnected()) { + getTelecomService().handleCallIntent(intent); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException handleCallIntent: " + e); + } + + } + private ITelecomService getTelecomService() { if (mTelecomServiceOverride != null) { return mTelecomServiceOverride; diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 38247bc80e5c..df7d6832833a 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -284,4 +284,9 @@ interface ITelecomService { * @see TelecomServiceImpl#isInEmergencyCall */ boolean isInEmergencyCall(); + + /** + * @see TelecomServiceImpl#handleCallIntent + */ + void handleCallIntent(in Intent intent); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d8743bcfd21d..6eaecc6760bc 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -271,6 +271,14 @@ public class CarrierConfigManager { KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool"; /** + * Do only allow auto selection in Advanced Network Settings when in home network. + * Manual selection is allowed when in roaming network. + * @hide + */ + public static final String + KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network"; + + /** * Control whether users receive a simplified network settings UI and improved network * selection. */ @@ -1625,11 +1633,21 @@ public class CarrierConfigManager { * When {@code false}, use default title for Enhanced 4G LTE Mode settings. * When {@code true}, use the variant. * @hide + * @deprecated use {@link #KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT}. */ + @Deprecated public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL = "enhanced_4g_lte_title_variant_bool"; /** + * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings. + * Default value is 0, which indicates the default title string. + * @hide + */ + public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = + "enhanced_4g_lte_title_variant_int"; + + /** * Indicates whether the carrier wants to notify the user when handover of an LTE video call to * WIFI fails. * <p> @@ -2182,6 +2200,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true); sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false); sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false); + sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false); sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false); sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false); @@ -2411,6 +2430,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null); sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false); + sDefaults.putInt(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 0); sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false); sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null); sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false); diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index ee5cdc2780db..d7169b23d94b 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -319,6 +319,29 @@ public class DisconnectCause { */ public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; + /** + * Indicates that a new outgoing call cannot be placed because there is already an outgoing + * call dialing out. + */ + public static final int ALREADY_DIALING = 72; + + /** + * Indicates that a new outgoing call cannot be placed while there is a ringing call. + */ + public static final int CANT_CALL_WHILE_RINGING = 73; + + /** + * Indicates that a new outgoing call cannot be placed because calling has been disabled using + * the ro.telephony.disable-call system property. + */ + public static final int CALLING_DISABLED = 74; + + /** + * Indicates that a new outgoing call cannot be placed because there is currently an ongoing + * foreground and background call. + */ + public static final int TOO_MANY_ONGOING_CALLS = 75; + //********************************************************************************************* // When adding a disconnect type: // 1) Update toString() with the newly added disconnect type. @@ -474,6 +497,14 @@ public class DisconnectCause { return "NORMAL_UNSPECIFIED"; case IMS_SIP_ALTERNATE_EMERGENCY_CALL: return "IMS_SIP_ALTERNATE_EMERGENCY_CALL"; + case ALREADY_DIALING: + return "ALREADY_DIALING"; + case CANT_CALL_WHILE_RINGING: + return "CANT_CALL_WHILE_RINGING"; + case CALLING_DISABLED: + return "CALLING_DISABLED"; + case TOO_MANY_ONGOING_CALLS: + return "TOO_MANY_ONGOING_CALLS"; default: return "INVALID: " + cause; } diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java index 8e99518d78b8..79298fd54c50 100644 --- a/telephony/java/android/telephony/NeighboringCellInfo.java +++ b/telephony/java/android/telephony/NeighboringCellInfo.java @@ -32,7 +32,12 @@ import android.os.Parcelable; /** * Represents the neighboring cell information, including * Received Signal Strength and Cell ID location. + * + * @deprecated This class should not be used by any app targeting + * {@link Build.VERSION_CODES.Q Android Q} or higher. Instead callers should use + * {@Link android.telephony.CellInfo CellInfo}. */ +@Deprecated public class NeighboringCellInfo implements Parcelable { /** diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index f2b73dccee2d..7469186a5d51 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.NetworkRegistrationState.Domain; import android.text.TextUtils; import java.lang.annotation.Retention; @@ -1595,7 +1596,7 @@ public class ServiceState implements Parcelable { /** * Get all of the available network registration states. * - * @return List of registration states + * @return List of {@link NetworkRegistrationState} * @hide */ @SystemApi @@ -1606,14 +1607,30 @@ public class ServiceState implements Parcelable { } /** - * Get the network registration states with given transport type. + * Get the network registration states for the transport type. * - * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType} - * @return List of registration states. + * @param transportType The {@link AccessNetworkConstants.TransportType transport type} + * @return List of {@link NetworkRegistrationState} * @hide + * + * @deprecated Use {@link #getNetworkRegistrationStatesFromTransportType(int)} */ + @Deprecated @SystemApi public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) { + return getNetworkRegistrationStatesForTransportType(transportType); + } + + /** + * Get the network registration states for the transport type. + * + * @param transportType The {@link AccessNetworkConstants.TransportType transport type} + * @return List of {@link NetworkRegistrationState} + * @hide + */ + @SystemApi + public List<NetworkRegistrationState> getNetworkRegistrationStatesForTransportType( + int transportType) { List<NetworkRegistrationState> list = new ArrayList<>(); synchronized (mNetworkRegistrationStates) { @@ -1628,16 +1645,57 @@ public class ServiceState implements Parcelable { } /** - * Get the network registration states with given transport type and domain. + * Get the network registration states for the network domain. * - * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or - * {@link NetworkRegistrationState#DOMAIN_PS}. - * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType} - * @return The matching NetworkRegistrationState. + * @param domain The network {@link NetworkRegistrationState.Domain domain} + * @return List of {@link NetworkRegistrationState} * @hide */ @SystemApi - public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) { + public List<NetworkRegistrationState> getNetworkRegistrationStatesForDomain( + @Domain int domain) { + List<NetworkRegistrationState> list = new ArrayList<>(); + + synchronized (mNetworkRegistrationStates) { + for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { + if (networkRegistrationState.getDomain() == domain) { + list.add(networkRegistrationState); + } + } + } + + return list; + } + + /** + * Get the network registration state for the transport type and network domain. + * + * @param domain The network {@link NetworkRegistrationState.Domain domain} + * @param transportType The {@link AccessNetworkConstants.TransportType transport type} + * @return The matching {@link NetworkRegistrationState} + * @hide + * + * @deprecated Use {@link #getNetworkRegistrationState(int, int)} + */ + @Deprecated + @SystemApi + public NetworkRegistrationState getNetworkRegistrationStates(@Domain int domain, + int transportType) { + return getNetworkRegistrationState(domain, transportType); + } + + /** + * Get the network registration state for the transport type and network domain. + * + * @param domain The network {@link NetworkRegistrationState.Domain domain} + * @param transportType The {@link AccessNetworkConstants.TransportType transport type} + * @return The matching {@link NetworkRegistrationState} + * @hide + * + */ + @SystemApi + public NetworkRegistrationState getNetworkRegistrationState(@Domain int domain, + int transportType) { synchronized (mNetworkRegistrationStates) { for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) { if (networkRegistrationState.getTransportType() == transportType @@ -1669,5 +1727,4 @@ public class ServiceState implements Parcelable { mNetworkRegistrationStates.add(regState); } } - } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 92030efb1a28..bba4868c54c1 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -166,7 +166,6 @@ public class TelephonyManager { /** @hide */ static public final int OTASP_SIM_UNPROVISIONED = 5; - /** @hide */ static public final int KEY_TYPE_EPDG = 1; @@ -1579,6 +1578,7 @@ public class TelephonyManager { * * @return List of NeighboringCellInfo or null if info unavailable. * + * @removed * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information * from NeighboringCellInfo, including LTE cell information. */ @@ -2913,7 +2913,7 @@ public class TelephonyManager { * of time the mode may be unknown. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE} * or {@link PhoneConstants#LTE_ON_CDMA_TRUE} @@ -5706,23 +5706,6 @@ public class TelephonyManager { } /** - * @return true if the IMS resolver is busy resolving a binding and should not be considered - * available, false if the IMS resolver is idle. - * @hide - */ - public boolean isResolvingImsBinding() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.isResolvingImsBinding(); - } - } catch (RemoteException e) { - Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage()); - } - return false; - } - - /** * Set IMS registration state * * @param Registration state @@ -5965,7 +5948,7 @@ public class TelephonyManager { * Sets the network selection mode to automatic. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling @@ -5990,7 +5973,7 @@ public class TelephonyManager { * Perform a radio scan and return the list of available networks. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p> Note that this scan can take a long time (sometimes minutes) to happen. * @@ -6069,7 +6052,7 @@ public class TelephonyManager { * Ask the radio to connect to the input network and change selection mode to manual. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling @@ -6094,7 +6077,7 @@ public class TelephonyManager { * Ask the radio to connect to the input network and change selection mode to manual. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling @@ -6127,7 +6110,7 @@ public class TelephonyManager { * Get the network selection mode. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * @return the network selection mode. * @@ -7017,7 +7000,8 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - isDataRoamingEnabled = telephony.isDataRoamingEnabled(getSubId()); + isDataRoamingEnabled = telephony.isDataRoamingEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#isDataRoamingEnabled", e); @@ -7029,7 +7013,7 @@ public class TelephonyManager { * Gets the roaming mode for CDMA phone. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * @return one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, {@link #CDMA_ROAMING_MODE_HOME}, * {@link #CDMA_ROAMING_MODE_AFFILIATED}, {@link #CDMA_ROAMING_MODE_ANY}. @@ -7054,7 +7038,7 @@ public class TelephonyManager { * Sets the roaming mode for CDMA phone to the given mode {@code mode}. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * @param mode should be one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, * {@link #CDMA_ROAMING_MODE_HOME}, {@link #CDMA_ROAMING_MODE_AFFILIATED}, @@ -7123,7 +7107,8 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.setDataRoamingEnabled(getSubId(), isEnabled); + telephony.setDataRoamingEnabled( + getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), isEnabled); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#setDataRoamingEnabled", e); @@ -7916,7 +7901,7 @@ public class TelephonyManager { * Returns the current {@link ServiceState} information. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). @@ -8385,7 +8370,7 @@ public class TelephonyManager { * Checks if phone is in emergency callback mode. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the - * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()} + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} * * @return true if phone is in emergency callback mode. * @hide diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 0e4a7addd58c..951bed468eda 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -31,6 +31,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; @@ -1205,7 +1206,8 @@ public class ApnSetting implements Parcelable { /** @hide */ public static int getMvnoTypeIntFromString(String mvnoType) { - Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoType); + String mvnoTypeString = TextUtils.isEmpty(mvnoType) ? mvnoType : mvnoType.toLowerCase(); + Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString); return mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt; } diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.aidl b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl new file mode 100644 index 000000000000..bfb0a59b16c8 --- /dev/null +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.emergency; + +parcelable EmergencyNumber; diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java new file mode 100644 index 000000000000..d6a08543b9cd --- /dev/null +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.emergency; + +import android.annotation.IntDef; +import android.hardware.radio.V1_3.EmergencyNumberSource; +import android.hardware.radio.V1_3.EmergencyServiceCategory; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * A parcelable class that wraps and retrieves the information of number, service category(s) and + * country code for a specific emergency number. + */ +public final class EmergencyNumber implements Parcelable { + + private static final String LOG_TAG = "EmergencyNumber"; + + /** + * Defining Emergency Service Category as follows: + * - General emergency call, all categories; + * - Police; + * - Ambulance; + * - Fire Brigade; + * - Marine Guard; + * - Mountain Rescue; + * - Manually Initiated eCall (MIeC); + * - Automatically Initiated eCall (AIeC); + * + * Category UNSPECIFIED (General emergency call, all categories) indicates that no specific + * services are associated with this emergency number; if the emergency number is specified, + * it has one or more defined emergency service categories. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + * + * @hide + */ + @IntDef(flag = true, prefix = { "EMERGENCY_SERVICE_CATEGORY_" }, value = { + EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED, + EMERGENCY_SERVICE_CATEGORY_POLICE, + EMERGENCY_SERVICE_CATEGORY_AMBULANCE, + EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE, + EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD, + EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE, + EMERGENCY_SERVICE_CATEGORY_MIEC, + EMERGENCY_SERVICE_CATEGORY_AIEC + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EmergencyServiceCategories {} + + /** + * Emergency Service Category UNSPECIFIED (General emergency call, all categories) bit-field + * indicates that no specific services are associated with this emergency number; if the + * emergency number is specified, it has one or more defined emergency service categories. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = + EmergencyServiceCategory.UNSPECIFIED; + /** + * Bit-field that indicates Emergency Service Category for Police. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = EmergencyServiceCategory.POLICE; + /** + * Bit-field that indicates Emergency Service Category for Ambulance. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE = + EmergencyServiceCategory.AMBULANCE; + /** + * Bit-field that indicates Emergency Service Category for Fire Brigade. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = + EmergencyServiceCategory.FIRE_BRIGADE; + /** + * Bit-field that indicates Emergency Service Category for Marine Guard. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = + EmergencyServiceCategory.MARINE_GUARD; + /** + * Bit-field that indicates Emergency Service Category for Mountain Rescue. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = + EmergencyServiceCategory.MOUNTAIN_RESCUE; + /** + * Bit-field that indicates Emergency Service Category for Manually Initiated eCall (MIeC) + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = EmergencyServiceCategory.MIEC; + /** + * Bit-field that indicates Emergency Service Category for Automatically Initiated eCall (AIeC) + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = EmergencyServiceCategory.AIEC; + + private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET; + static { + EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>(); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_POLICE); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AMBULANCE); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MIEC); + EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AIEC); + } + + /** + * The source to tell where the corresponding @1.3::EmergencyNumber comes from. + * + * The emergency number has one or more defined emergency number sources. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + * + * @hide + */ + @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = { + EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING, + EMERGENCY_NUMBER_SOURCE_SIM, + EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG, + EMERGENCY_NUMBER_SOURCE_DEFAULT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EmergencyNumberSources {} + + /** + * Bit-field which indicates the number is from the network signaling. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = + EmergencyNumberSource.NETWORK_SIGNALING; + /** + * Bit-field which indicates the number is from the sim. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM; + /** Bit-field which indicates the number is from the modem config. */ + public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = + EmergencyNumberSource.MODEM_CONFIG; + /** + * Bit-field which indicates the number is available as default. + * + * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be + * available when sim is not present. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls + */ + public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = EmergencyNumberSource.DEFAULT; + + private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET; + static { + EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>(); + EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING); + EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM); + EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG); + EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT); + } + + private final String mNumber; + private final String mCountryIso; + private final int mEmergencyServiceCategoryBitmask; + private final int mEmergencyNumberSourceBitmask; + + /** @hide */ + public EmergencyNumber(String number, String countryIso, + int emergencyServiceCategories, + int emergencyNumberSources) { + this.mNumber = number; + this.mCountryIso = countryIso; + this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories; + this.mEmergencyNumberSourceBitmask = emergencyNumberSources; + } + + /** @hide */ + public EmergencyNumber(Parcel source) { + mNumber = source.readString(); + mCountryIso = source.readString(); + mEmergencyServiceCategoryBitmask = source.readInt(); + mEmergencyNumberSourceBitmask = source.readInt(); + } + + /** + * Get the dialing number of the emergency number. + * + * The character in the number string is only the dial pad + * character('0'-'9', '*', or '#'). For example: 911. + * + * @return the dialing number. + */ + public String getNumber() { + return mNumber; + } + + /** + * Get the country code string (lowercase character) in ISO 3166 format of the emergency number. + * + * @return the country code string (lowercase character) in ISO 3166 format. + */ + public String getCountryIso() { + return mCountryIso; + } + + /** + * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of + * the emergency number. + * + * @return bitmask of the emergency service categories {@link EmergencyServiceCategories} + */ + public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() { + return mEmergencyServiceCategoryBitmask; + } + + /** + * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency + * number. + * + * @return a list of the emergency service categories {@link EmergencyServiceCategories} + */ + public List<Integer> getEmergencyServiceCategories() { + List<Integer> categories = new ArrayList<>(); + if (serviceUnspecified()) { + categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED); + return categories; + } + for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) { + if (isInEmergencyServiceCategories(category)) { + categories.add(category); + } + } + return categories; + } + + /** + * Checks if the emergency service category is unspecified for the emergency number + * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}. + * + * @return {@code true} if the emergency service category is unspecified for the emergency + * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise. + */ + private boolean serviceUnspecified() { + return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED; + } + + /** + * Checks if the emergency number is in the specified emergency service category(s) + * {@link EmergencyServiceCategories}. + * + * @return {@code true} if the emergency number is in the specified emergency service + * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise. + * + * @param categories - emergency service categories {@link EmergencyServiceCategories} + */ + public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) { + if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) { + return serviceUnspecified(); + } + return (mEmergencyServiceCategoryBitmask & categories) == categories; + } + + /** + * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number. + * + * @return bitmask of the emergency number sources {@link EmergencyNumberSources} + */ + public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() { + return mEmergencyNumberSourceBitmask; + } + + /** + * Returns a list of {@link EmergencyNumberSources} of the emergency number. + * + * @return a list of {@link EmergencyNumberSources} + */ + public List<Integer> getEmergencyNumberSources() { + List<Integer> sources = new ArrayList<>(); + for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) { + if ((mEmergencyNumberSourceBitmask & source) == source) { + sources.add(source); + } + } + return sources; + } + + /** + * Checks if the emergency number is from the specified emergency number source(s) + * {@link EmergencyNumberSources}. + * + * @return {@code true} if the emergency number is from the specified emergency number + * source(s) {@link EmergencyNumberSources}; {@code false} otherwise. + * + * @param sources - {@link EmergencyNumberSources} + */ + public boolean isFromSources(@EmergencyNumberSources int sources) { + return (mEmergencyNumberSourceBitmask & sources) == sources; + } + + @Override + /** @hide */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mNumber); + dest.writeString(mCountryIso); + dest.writeInt(mEmergencyServiceCategoryBitmask); + dest.writeInt(mEmergencyNumberSourceBitmask); + } + + @Override + /** @hide */ + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso + + " / [ServiceCategories]" + + Integer.toBinaryString(mEmergencyServiceCategoryBitmask) + + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask); + } + + @Override + public boolean equals(Object o) { + if (!EmergencyNumber.class.isInstance(o)) { + return false; + } + return (o == this || toString().equals(o.toString())); + } + + public static final Parcelable.Creator<EmergencyNumber> CREATOR = + new Parcelable.Creator<EmergencyNumber>() { + @Override + public EmergencyNumber createFromParcel(Parcel in) { + return new EmergencyNumber(in); + } + + @Override + public EmergencyNumber[] newArray(int size) { + return new EmergencyNumber[size]; + } + }; +} diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 5d6a8c158eed..89ef33914c12 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -117,12 +117,14 @@ public final class ImsCallProfile implements Parcelable { * @hide */ public static final String EXTRA_CONFERENCE = "conference"; + /** * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an * emergency call. The {@link ImsService} sets this on a call to indicate that the network has * identified the call as an emergency call. */ - public static final String EXTRA_E_CALL = "e_call"; + public static final String EXTRA_EMERGENCY_CALL = "e_call"; + /** * @hide */ diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java index a20d4f5ad27b..df903cc270a2 100644 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -16,22 +16,16 @@ package android.telephony.ims; -import android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.SystemApi; import android.os.Message; import android.os.RemoteException; import android.telephony.ims.aidl.IImsCallSessionListener; - -import java.util.Objects; -import java.util.concurrent.Executor; - -import android.telephony.ims.stub.ImsCallSessionImplBase; import android.util.Log; import com.android.ims.internal.IImsCallSession; import com.android.ims.internal.IImsVideoCallProvider; +import java.util.Objects; + /** * Provides the call initiation/termination, and media exchange between two IMS endpoints. * It directly communicates with IMS service which implements the IMS protocol behavior. @@ -42,7 +36,8 @@ public class ImsCallSession { private static final String TAG = "ImsCallSession"; /** - * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition. + * Defines IMS call session state. Please use + * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition. * This is kept around for capability reasons. */ public static class State { @@ -1027,9 +1022,9 @@ public class ImsCallSession { } /** - * Sends RTT Upgrade request + * Sends RTT Upgrade or downgrade request * - * @param to : expected profile + * @param to Profile with the RTT flag set to the desired value */ public void sendRttModifyRequest(ImsCallProfile to) { if (mClosed) { diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 31381804d143..cecf2e26f139 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -21,12 +21,11 @@ import android.annotation.SystemApi; import android.net.Uri; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.util.Log; -import android.telephony.ims.ImsReasonInfo; - import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; @@ -81,13 +80,14 @@ public class ImsRegistrationImplBase { * Callback class for receiving Registration callback events. * @hide */ - public static class Callback { + public static class Callback extends IImsRegistrationCallback.Stub { /** * Notifies the framework when the IMS Provider is connected to the IMS network. * * @param imsRadioTech the radio access technology. Valid values are defined in * {@link ImsRegistrationTech}. */ + @Override public void onRegistered(@ImsRegistrationTech int imsRadioTech) { } @@ -97,6 +97,7 @@ public class ImsRegistrationImplBase { * @param imsRadioTech the radio access technology. Valid values are defined in * {@link ImsRegistrationTech}. */ + @Override public void onRegistering(@ImsRegistrationTech int imsRadioTech) { } @@ -105,6 +106,7 @@ public class ImsRegistrationImplBase { * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. */ + @Override public void onDeregistered(ImsReasonInfo info) { } @@ -115,6 +117,7 @@ public class ImsRegistrationImplBase { * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed * @param info A {@link ImsReasonInfo} that identifies the reason for failure. */ + @Override public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) { } @@ -125,6 +128,7 @@ public class ImsRegistrationImplBase { * @param uris new array of subscriber {@link Uri}s that are associated with this IMS * subscription. */ + @Override public void onSubscriberAssociatedUriChanged(Uri[] uris) { } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 33616149c3b4..627dfaa5d36e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -707,12 +707,6 @@ interface ITelephony { IImsConfig getImsConfig(int slotId, int feature); /** - * @return true if the IMS resolver is busy resolving a binding and should not be considered - * available, false if the IMS resolver is idle. - */ - boolean isResolvingImsBinding(); - - /** * @return true if the ImsService to bind to for the slot id specified was set, false otherwise. */ boolean setImsService(int slotId, boolean isCarrierImsService, String packageName); diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index f9de776f9a7b..21f3b92c6c4f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -176,6 +176,10 @@ public class PhoneConstants { // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is // FIXME: extensive, and see if this should be an array of all active subId's or ...? + /** + * @Deprecated use {@link android.telephony.SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} + * instead. + */ public static final String SUBSCRIPTION_KEY = "subscription"; public static final String SUB_SETTING = "subSettings"; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java index 6bf22a05beec..8015b07fa024 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java @@ -33,6 +33,7 @@ import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsConstants; import java.io.UnsupportedEncodingException; +import java.util.Locale; /** * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is @@ -44,16 +45,34 @@ public class GsmSmsCbMessage { * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. */ private static final String[] LANGUAGE_CODES_GROUP_0 = { - "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu", - "pl", null + Locale.GERMAN.getLanguage(), // German + Locale.ENGLISH.getLanguage(), // English + Locale.ITALIAN.getLanguage(), // Italian + Locale.FRENCH.getLanguage(), // French + new Locale("es").getLanguage(), // Spanish + new Locale("nl").getLanguage(), // Dutch + new Locale("sv").getLanguage(), // Swedish + new Locale("da").getLanguage(), // Danish + new Locale("pt").getLanguage(), // Portuguese + new Locale("fi").getLanguage(), // Finnish + new Locale("nb").getLanguage(), // Norwegian + new Locale("el").getLanguage(), // Greek + new Locale("tr").getLanguage(), // Turkish + new Locale("hu").getLanguage(), // Hungarian + new Locale("pl").getLanguage(), // Polish + null }; /** * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. */ private static final String[] LANGUAGE_CODES_GROUP_2 = { - "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null, - null, null + new Locale("cs").getLanguage(), // Czech + new Locale("he").getLanguage(), // Hebrew + new Locale("ar").getLanguage(), // Arabic + new Locale("ru").getLanguage(), // Russian + new Locale("is").getLanguage(), // Icelandic + null, null, null, null, null, null, null, null, null, null, null }; private static final char CARRIAGE_RETURN = 0x0d; diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java index 0fabc2ff6b86..541ca8d1e5c0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java @@ -118,71 +118,103 @@ public class SmsCbConstants { public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE = 0x111E; // 4382 - /** CMAS Message Identifier for Presidential Level alerts for additional languages - * for additional languages. */ + /** + * CMAS Message Identifier for Presidential Level alerts for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE = 0x111F; // 4383 - /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE = 0x1120; // 4384 - /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE = 0x1121; // 4385 - /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE = 0x1122; // 4386 - /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE = 0x1123; // 4387 - /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE = 0x1124; // 4388 - /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely - * for additional languages.*/ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE = 0x1125; // 4389 - /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE = 0x1126; // 4390 - /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely - * for additional languages.*/ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE = 0x1127; // 4391 - /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert) - * for additional languages. */ + /** + * CMAS Message Identifier for Child Abduction Emergency (Amber Alert) + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE = 0x1128; // 4392 - /** CMAS Message Identifier for the Required Monthly Test - * for additional languages. */ + /** CMAS Message Identifier for the Required Monthly Test for additional languages. */ public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE = 0x1129; // 4393 - /** CMAS Message Identifier for CMAS Exercise - * for additional languages. */ + /** CMAS Message Identifier for CMAS Exercise for additional languages. */ public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE = 0x112A; // 4394 - /** CMAS Message Identifier for operator defined use - * for additional languages. */ + /** CMAS Message Identifier for operator defined use for additional languages. */ public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE = 0x112B; // 4395 + /** CMAS Message Identifier for CMAS Public Safety Alerts. */ + public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY + = 0x112C; // 4396 + + /** CMAS Message Identifier for CMAS Public Safety Alerts for additional languages. */ + public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY_LANGUAGE + = 0x112D; // 4397 + + /** CMAS Message Identifier for CMAS State/Local Test. */ + public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST + = 0x112E; // 4398 + + /** CMAS Message Identifier for CMAS State/Local Test for additional languages. */ + public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST_LANGUAGE + = 0x112F; // 4399 + /** End of CMAS Message Identifier range (including future extensions). */ public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER = 0x112F; // 4399 diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk index fe65eccedd24..c225e170c377 100644 --- a/tests/NetworkSecurityConfigTest/Android.mk +++ b/tests/NetworkSecurityConfigTest/Android.mk @@ -7,7 +7,6 @@ LOCAL_CERTIFICATE := platform LOCAL_JAVA_LIBRARIES := \ android.test.runner \ - bouncycastle \ conscrypt \ android.test.base \ diff --git a/tests/net/Android.mk b/tests/net/Android.mk index e529b933bb80..750e2fb6f6b4 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -38,6 +38,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libbacktrace \ libbase \ libbinder \ + libbinderthreadstate \ libc++ \ libcrypto \ libcutils \ diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java index 312b3d1878d6..a592809618e6 100644 --- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java +++ b/tests/net/java/android/net/dhcp/DhcpPacketTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.annotation.Nullable; import android.net.DhcpResults; import android.net.LinkAddress; import android.net.NetworkUtils; @@ -37,6 +38,7 @@ import com.android.internal.util.HexDump; import java.io.ByteArrayOutputStream; import java.net.Inet4Address; import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -56,6 +58,8 @@ public class DhcpPacketTest { private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH); private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress( SERVER_ADDR, PREFIX_LENGTH); + private static final String HOSTNAME = "testhostname"; + private static final short MTU = 1500; // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code // doesn't use == instead of equals when comparing addresses. private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0"); @@ -960,7 +964,8 @@ public class DhcpPacketTest { assertTrue(msg, Arrays.equals(expected, actual)); } - public void checkBuildOfferPacket(int leaseTimeSecs) throws Exception { + public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname) + throws Exception { final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2); final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000); final int transactionId = 0xdeadbeef; @@ -971,7 +976,8 @@ public class DhcpPacketTest { CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */, BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */, Collections.singletonList(SERVER_ADDR) /* dnsServers */, - SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, false /* metered */); + SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname, + false /* metered */, MTU); ByteArrayOutputStream bos = new ByteArrayOutputStream(); // BOOTP headers @@ -1027,12 +1033,22 @@ public class DhcpPacketTest { // Nameserver bos.write(new byte[] { (byte) 0x06, (byte) 0x04 }); bos.write(SERVER_ADDR.getAddress()); + // Hostname + if (hostname != null) { + bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()}); + bos.write(hostname.getBytes(Charset.forName("US-ASCII"))); + } + // MTU + bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 }); + bos.write(shortToByteArray(MTU)); // End options. bos.write(0xff); - final byte[] expected = bos.toByteArray(); - assertTrue((expected.length & 1) == 0); + if ((bos.size() & 1) != 0) { + bos.write(0x00); + } + final byte[] expected = bos.toByteArray(); final byte[] actual = new byte[packet.limit()]; packet.get(actual); final String msg = "Expected:\n " + HexDump.dumpHexString(expected) + @@ -1042,13 +1058,18 @@ public class DhcpPacketTest { @Test public void testOfferPacket() throws Exception { - checkBuildOfferPacket(3600); - checkBuildOfferPacket(Integer.MAX_VALUE); - checkBuildOfferPacket(0x80000000); - checkBuildOfferPacket(INFINITE_LEASE); + checkBuildOfferPacket(3600, HOSTNAME); + checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME); + checkBuildOfferPacket(0x80000000, HOSTNAME); + checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME); + checkBuildOfferPacket(3600, null); } private static byte[] intToByteArray(int val) { return ByteBuffer.allocate(4).putInt(val).array(); } + + private static byte[] shortToByteArray(short val) { + return ByteBuffer.allocate(2).putShort(val).array(); + } } diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java index 45a50d9a8b5f..df34c7310b63 100644 --- a/tests/net/java/android/net/dhcp/DhcpServerTest.java +++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java @@ -17,6 +17,7 @@ package android.net.dhcp; import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; +import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; import static android.net.dhcp.DhcpPacket.INADDR_ANY; import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; @@ -87,6 +88,7 @@ public class DhcpServerTest { Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201"))); private static final long TEST_LEASE_TIME_SECS = 3600L; private static final int TEST_MTU = 1500; + private static final String TEST_HOSTNAME = "testhostname"; private static final int TEST_TRANSACTION_ID = 123; private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 }; @@ -96,7 +98,10 @@ public class DhcpServerTest { private static final long TEST_CLOCK_TIME = 1234L; private static final int TEST_LEASE_EXPTIME_SECS = 3600; private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC, - TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS*1000L + TEST_CLOCK_TIME, null /* hostname */); + TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, + null /* hostname */); + private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC, + TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME); @NonNull @Mock private Dependencies mDeps; @@ -217,15 +222,17 @@ public class DhcpServerTest { public void testRequest_Selecting_Ack() throws Exception { when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC), eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */, - eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */)) - .thenReturn(TEST_LEASE); + eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME))) + .thenReturn(TEST_LEASE_WITH_HOSTNAME); final DhcpRequestPacket request = makeRequestSelectingPacket(); + request.mHostName = TEST_HOSTNAME; + request.mRequestedParams = new byte[] { DHCP_HOST_NAME }; mServer.processPacket(request, DHCP_CLIENT); assertResponseSentTo(TEST_CLIENT_ADDR); final DhcpAckPacket packet = assertAck(getPacket()); - assertMatchesTestLease(packet); + assertMatchesTestLease(packet, TEST_HOSTNAME); } @Test @@ -270,14 +277,18 @@ public class DhcpServerTest { * - other request states (init-reboot/renewing/rebinding) */ - private void assertMatchesTestLease(@NonNull DhcpPacket packet) { + private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) { assertMatchesClient(packet); assertFalse(packet.hasExplicitClientId()); assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier); assertEquals(TEST_CLIENT_ADDR, packet.mYourIp); assertNotNull(packet.mLeaseTime); assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime); - assertNull(packet.mHostName); + assertEquals(hostname, packet.mHostName); + } + + private void assertMatchesTestLease(@NonNull DhcpPacket packet) { + assertMatchesTestLease(packet, null); } private void assertMatchesClient(@NonNull DhcpPacket packet) { diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java index 39ecb7e5a45e..122edbaf078c 100644 --- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java +++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java @@ -69,17 +69,12 @@ public class InetDiagSocketTest { private ConnectivityManager mCm; private Context mContext; private final static int SOCKET_TIMEOUT_MS = 100; - private boolean mInetDiagUdpEnabled; @Before public void setUp() throws Exception { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); mContext = instrumentation.getTargetContext(); mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - int expectedUid = Process.myUid(); - UdpConnection udp = new UdpConnection("127.0.0.1", "127.0.0.2"); - int uid = mCm.getConnectionOwnerUid(udp.protocol, udp.local, udp.remote); - mInetDiagUdpEnabled = (uid == expectedUid); } private class Connection { @@ -188,11 +183,6 @@ public class InetDiagSocketTest { tcp.close(); /** - * TODO: STOPSHIP: Always test for UDP, do not allow opt-out. - */ - if (!mInetDiagUdpEnabled) return; - - /** * For UDP connections, either a complete match {protocol, local, remote} or a * partial match {protocol, local} should return a valid UID. */ diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e3db7e8a1354..1c77fcc568f6 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -79,6 +79,7 @@ import static org.mockito.Mockito.when; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -248,7 +249,7 @@ public class ConnectivityServiceTest { @Spy private Resources mResources; private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>(); - MockContext(Context base) { + MockContext(Context base, ContentProvider settingsProvider) { super(base); mResources = spy(base.getResources()); @@ -260,7 +261,7 @@ public class ConnectivityServiceTest { }); mContentResolver = new MockContentResolver(); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider); } @Override @@ -1048,7 +1049,9 @@ public class ConnectivityServiceTest { Looper.prepare(); } - mServiceContext = new MockContext(InstrumentationRegistry.getContext()); + FakeSettingsProvider.clearSettingsProvider(); + mServiceContext = new MockContext(InstrumentationRegistry.getContext(), + new FakeSettingsProvider()); LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService( NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class)); @@ -1067,13 +1070,13 @@ public class ConnectivityServiceTest { // Ensure that the default setting for Captive Portals is used for most tests setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); } @After public void tearDown() throws Exception { - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); mCellNetworkAgent = null; @@ -1086,6 +1089,7 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.disconnect(); mEthernetNetworkAgent = null; } + FakeSettingsProvider.clearSettingsProvider(); } private static int transportToLegacyType(int transport) { @@ -2023,7 +2027,7 @@ public class ConnectivityServiceTest { @Test public void testNetworkGoesIntoBackgroundAfterLinger() { - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() .build(); @@ -2768,10 +2772,10 @@ public class ConnectivityServiceTest { Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); } - private void setMobileDataAlwaysOn(boolean enable) { + private void setAlwaysOnNetworks(boolean enable) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0); - mService.updateMobileDataAlwaysOn(); + mService.updateAlwaysOnNetworks(); waitForIdle(); } @@ -2793,7 +2797,7 @@ public class ConnectivityServiceTest { public void testBackgroundNetworks() throws Exception { // Create a background request. We can't do this ourselves because ConnectivityService // doesn't have an API for it. So just turn on mobile data always on. - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); final NetworkRequest request = new NetworkRequest.Builder().build(); final NetworkRequest fgRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_FOREGROUND).build(); @@ -2991,7 +2995,7 @@ public class ConnectivityServiceTest { // Turn on mobile data always on. The factory starts looking again. testFactory.expectAddRequests(1); - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); testFactory.waitForNetworkRequests(2); assertTrue(testFactory.getMyStartRequested()); @@ -3011,7 +3015,7 @@ public class ConnectivityServiceTest { // Turn off mobile data always on and expect the request to disappear... testFactory.expectRemoveRequests(1); - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); testFactory.waitForNetworkRequests(1); // ... and cell data to be torn down. @@ -4532,4 +4536,78 @@ public class ConnectivityServiceTest { mCellNetworkAgent.disconnect(); mCm.unregisterNetworkCallback(networkCallback); } + + @Test + public void testDataActivityTracking() throws RemoteException { + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + mCellNetworkAgent.sendLinkProperties(cellLp); + reset(mNetworkManagementService); + mCellNetworkAgent.connect(true); + networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), + eq(ConnectivityManager.TYPE_MOBILE)); + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName(WIFI_IFNAME); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + + // Network switch + reset(mNetworkManagementService); + mWiFiNetworkAgent.connect(true); + networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(), + eq(ConnectivityManager.TYPE_WIFI)); + verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME)); + + // Disconnect wifi and switch back to cell + reset(mNetworkManagementService); + mWiFiNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + assertNoCallbacks(networkCallback); + verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); + verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), + eq(ConnectivityManager.TYPE_MOBILE)); + + // reconnect wifi + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + wifiLp.setInterfaceName(WIFI_IFNAME); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + mWiFiNetworkAgent.connect(true); + networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + + // Disconnect cell + reset(mNetworkManagementService); + mCellNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + // LOST callback is triggered earlier than removing idle timer. Broadcast should also be + // sent as network being switched. Ensure rule removal for cell will not be triggered + // unexpectedly before network being removed. + waitForIdle(); + verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME)); + verify(mNetworkManagementService, times(1)).removeNetwork( + eq(mCellNetworkAgent.getNetwork().netId)); + + // Disconnect wifi + ConditionVariable cv = waitForConnectivityBroadcasts(1); + reset(mNetworkManagementService); + mWiFiNetworkAgent.disconnect(); + waitFor(cv); + verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); + + // Clean up + mCm.unregisterNetworkCallback(networkCallback); + } } diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 99a5a69213fa..9b919abfa41d 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -34,8 +36,10 @@ import android.net.IpSecAlgorithm; import android.net.IpSecConfig; import android.net.IpSecManager; import android.net.IpSecSpiResponse; +import android.net.IpSecTransform; import android.net.IpSecTransformResponse; import android.net.IpSecTunnelInterfaceResponse; +import android.net.IpSecUdpEncapResponse; import android.net.LinkAddress; import android.net.Network; import android.net.NetworkUtils; @@ -62,16 +66,17 @@ public class IpSecServiceParameterizedTest { private static final int TEST_SPI = 0xD1201D; - private final String mDestinationAddr; private final String mSourceAddr; + private final String mDestinationAddr; private final LinkAddress mLocalInnerAddress; + private final int mFamily; @Parameterized.Parameters public static Collection ipSecConfigs() { return Arrays.asList( new Object[][] { - {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"}, - {"2601::2", "2601::10", "2001:db8::1/64"} + {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET}, + {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6} }); } @@ -129,12 +134,14 @@ public class IpSecServiceParameterizedTest { new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); private static final IpSecAlgorithm AEAD_ALGO = new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128); + private static final int REMOTE_ENCAP_PORT = 4500; public IpSecServiceParameterizedTest( - String sourceAddr, String destAddr, String localInnerAddr) { + String sourceAddr, String destAddr, String localInnerAddr, int family) { mSourceAddr = sourceAddr; mDestinationAddr = destAddr; mLocalInnerAddress = new LinkAddress(localInnerAddr); + mFamily = family; } @Before @@ -157,6 +164,8 @@ public class IpSecServiceParameterizedTest { .thenReturn(AppOpsManager.MODE_IGNORED); } + //TODO: Add a test to verify SPI. + @Test public void testIpSecServiceReserveSpi() throws Exception { when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI))) @@ -257,6 +266,47 @@ public class IpSecServiceParameterizedTest { config.setAuthentication(AUTH_ALGO); } + private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception { + config.setEncapType(IpSecTransform.ENCAP_ESPINUDP); + config.setEncapSocketResourceId(resourceId); + config.setEncapRemotePort(REMOTE_ENCAP_PORT); + } + + private void verifyTransformNetdCalledForCreatingSA( + IpSecConfig config, IpSecTransformResponse resp) throws Exception { + verifyTransformNetdCalledForCreatingSA(config, resp, 0); + } + + private void verifyTransformNetdCalledForCreatingSA( + IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception { + IpSecAlgorithm auth = config.getAuthentication(); + IpSecAlgorithm crypt = config.getEncryption(); + IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption(); + + verify(mMockNetd, times(1)) + .ipSecAddSecurityAssociation( + eq(mUid), + eq(config.getMode()), + eq(config.getSourceAddress()), + eq(config.getDestinationAddress()), + eq((config.getNetwork() != null) ? config.getNetwork().netId : 0), + eq(TEST_SPI), + eq(0), + eq(0), + eq((auth != null) ? auth.getName() : ""), + eq((auth != null) ? auth.getKey() : new byte[] {}), + eq((auth != null) ? auth.getTruncationLengthBits() : 0), + eq((crypt != null) ? crypt.getName() : ""), + eq((crypt != null) ? crypt.getKey() : new byte[] {}), + eq((crypt != null) ? crypt.getTruncationLengthBits() : 0), + eq((authCrypt != null) ? authCrypt.getName() : ""), + eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}), + eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0), + eq(config.getEncapType()), + eq(encapSocketPort), + eq(config.getEncapRemotePort())); + } + @Test public void testCreateTransform() throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); @@ -267,28 +317,7 @@ public class IpSecServiceParameterizedTest { mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); assertEquals(IpSecManager.Status.OK, createTransformResp.status); - verify(mMockNetd) - .ipSecAddSecurityAssociation( - eq(mUid), - anyInt(), - anyString(), - anyString(), - anyInt(), - eq(TEST_SPI), - anyInt(), - anyInt(), - eq(IpSecAlgorithm.AUTH_HMAC_SHA256), - eq(AUTH_KEY), - anyInt(), - eq(IpSecAlgorithm.CRYPT_AES_CBC), - eq(CRYPT_KEY), - anyInt(), - eq(""), - eq(new byte[] {}), - eq(0), - anyInt(), - anyInt(), - anyInt()); + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); } @Test @@ -302,28 +331,59 @@ public class IpSecServiceParameterizedTest { mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); assertEquals(IpSecManager.Status.OK, createTransformResp.status); - verify(mMockNetd) - .ipSecAddSecurityAssociation( - eq(mUid), - anyInt(), - anyString(), - anyString(), - anyInt(), - eq(TEST_SPI), - anyInt(), - anyInt(), - eq(""), - eq(new byte[] {}), - eq(0), - eq(""), - eq(new byte[] {}), - eq(0), - eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM), - eq(AEAD_KEY), - anyInt(), - anyInt(), - anyInt(), - anyInt()); + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp); + } + + @Test + public void testCreateTransportModeTransformWithEncap() throws Exception { + IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig); + + if (mFamily == AF_INET) { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port); + } else { + try { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); + fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6"); + } catch (IllegalArgumentException expected) { + } + } + } + + @Test + public void testCreateTunnelModeTransformWithEncap() throws Exception { + IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + IpSecConfig ipSecConfig = new IpSecConfig(); + ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); + addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); + addAuthAndCryptToIpSecConfig(ipSecConfig); + addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig); + + if (mFamily == AF_INET) { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port); + } else { + try { + IpSecTransformResponse createTransformResp = + mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage"); + fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6"); + } catch (IllegalArgumentException expected) { + } + } } @Test diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index 40d5544dccd8..a6ed9f252008 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -33,6 +33,7 @@ import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -75,6 +76,8 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.dhcp.DhcpServer; +import android.net.dhcp.DhcpServingParams; import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; @@ -85,6 +88,7 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.INetworkManagementService; +import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.test.TestLooper; @@ -146,6 +150,7 @@ public class TetheringTest { @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; + @Mock private DhcpServer mDhcpServer; @Mock private INetd mNetd; private final MockTetheringDependencies mTetheringDependencies = @@ -240,6 +245,12 @@ public class TetheringTest { public INetd getNetdService() { return mNetd; } + + @Override + public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, + DhcpServingParams params, SharedLog log) { + return mDhcpServer; + } }; } @@ -333,6 +344,7 @@ public class TetheringTest { mServiceContext = new MockContext(mContext); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -343,12 +355,16 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTetheringDependencies.reset(); - mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, - mLooper.getLooper(), mSystemProperties, - mTetheringDependencies); + mTethering = makeTethering(); verify(mNMService).registerTetheringStatsProvider(any(), anyString()); } + private Tethering makeTethering() { + return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, + mLooper.getLooper(), mSystemProperties, + mTetheringDependencies); + } + @After public void tearDown() { mServiceContext.unregisterReceiver(mBroadcastReceiver); @@ -597,6 +613,18 @@ public class TetheringTest { sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); + verify(mDhcpServer, times(1)).start(); + } + + @Test + public void workingMobileUsbTethering_IPv4LegacyDhcp() { + Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); + mTethering = makeTethering(); + final NetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + sendIPv6TetherUpdates(upstreamState); + + verify(mDhcpServer, never()).start(); } @Test @@ -620,6 +648,7 @@ public class TetheringTest { verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mRouterAdvertisementDaemon, times(1)).start(); + verify(mDhcpServer, times(1)).start(); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); @@ -633,6 +662,7 @@ public class TetheringTest { verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mDhcpServer, times(1)).start(); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); @@ -649,6 +679,7 @@ public class TetheringTest { runUsbTethering(upstreamState); verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mDhcpServer, times(1)).start(); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); // Then 464xlat comes up @@ -671,6 +702,8 @@ public class TetheringTest { // Forwarding was not re-added for v6 (still times(1)) verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + // DHCP not restarted on downstream (still times(1)) + verify(mDhcpServer, times(1)).start(); } @Test diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index bb312309b34f..521778484d91 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -225,13 +225,4 @@ public class TetheringConfigurationTest { final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); assertFalse(cfg.enableLegacyDhcpServer); } - - @Test - public void testNewDhcpServerDefault() { - Settings.Global.putString(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, null); - - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); - // TODO: change to false when new server is promoted to default - assertTrue(cfg.enableLegacyDhcpServer); - } } diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h index f71955247d78..b46a50398217 100644 --- a/tools/aapt2/ConfigDescription.h +++ b/tools/aapt2/ConfigDescription.h @@ -53,11 +53,11 @@ struct ConfigDescription : public android::ResTable_config { ConfigDescription(); ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit) ConfigDescription(const ConfigDescription& o); - ConfigDescription(ConfigDescription&& o); + ConfigDescription(ConfigDescription&& o) noexcept; ConfigDescription& operator=(const android::ResTable_config& o); ConfigDescription& operator=(const ConfigDescription& o); - ConfigDescription& operator=(ConfigDescription&& o); + ConfigDescription& operator=(ConfigDescription&& o) noexcept; ConfigDescription CopyWithoutSdkVersion() const; @@ -124,7 +124,7 @@ inline ConfigDescription::ConfigDescription(const ConfigDescription& o) { *static_cast<android::ResTable_config*>(this) = o; } -inline ConfigDescription::ConfigDescription(ConfigDescription&& o) { +inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept { *this = o; } @@ -141,7 +141,7 @@ inline ConfigDescription& ConfigDescription::operator=( return *this; } -inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) { +inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) noexcept { *this = o; return *this; } diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp index be67c9c8c03c..10e504ec0752 100644 --- a/tools/aapt2/java/ManifestClassGenerator.cpp +++ b/tools/aapt2/java/ManifestClassGenerator.cpp @@ -26,21 +26,20 @@ #include "util/Maybe.h" #include "xml/XmlDom.h" -using ::android::StringPiece; using ::aapt::text::IsJavaIdentifier; namespace aapt { -static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source, +static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source, const std::string& value) { - StringPiece result = value; + std::string result = value; size_t pos = value.rfind('.'); if (pos != std::string::npos) { result = result.substr(pos + 1); } // Normalize only the java identifier, leave the original value unchanged. - if (result.contains("-")) { + if (result.find("-") != std::string::npos) { result = JavaClassGenerator::TransformToFieldName(result); } @@ -64,7 +63,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element* return false; } - Maybe<StringPiece> result = + Maybe<std::string> result = ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value); if (!result) { return false; diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h index 30452552888e..d4b3abce68a7 100644 --- a/tools/aapt2/util/BigBuffer.h +++ b/tools/aapt2/util/BigBuffer.h @@ -68,7 +68,7 @@ class BigBuffer { */ explicit BigBuffer(size_t block_size); - BigBuffer(BigBuffer&& rhs); + BigBuffer(BigBuffer&& rhs) noexcept; /** * Number of occupied bytes in all the allocated blocks. @@ -136,7 +136,7 @@ class BigBuffer { inline BigBuffer::BigBuffer(size_t block_size) : block_size_(block_size), size_(0) {} -inline BigBuffer::BigBuffer(BigBuffer&& rhs) +inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept : block_size_(rhs.block_size_), size_(rhs.size_), blocks_(std::move(rhs.blocks_)) {} diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h index 59858e492c4c..1727b18e4106 100644 --- a/tools/aapt2/util/ImmutableMap.h +++ b/tools/aapt2/util/ImmutableMap.h @@ -32,8 +32,8 @@ class ImmutableMap { using const_iterator = typename std::vector<std::pair<TKey, TValue>>::const_iterator; - ImmutableMap(ImmutableMap&&) = default; - ImmutableMap& operator=(ImmutableMap&&) = default; + ImmutableMap(ImmutableMap&&) noexcept = default; + ImmutableMap& operator=(ImmutableMap&&) noexcept = default; static ImmutableMap<TKey, TValue> CreatePreSorted( std::initializer_list<std::pair<TKey, TValue>> list) { diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index 9a82418e0a5a..031276c8b885 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -46,7 +46,7 @@ class Maybe { template <typename U> Maybe(const Maybe<U>& rhs); // NOLINT(implicit) - Maybe(Maybe&& rhs); + Maybe(Maybe&& rhs) noexcept; template <typename U> Maybe(Maybe<U>&& rhs); // NOLINT(implicit) @@ -56,7 +56,7 @@ class Maybe { template <typename U> Maybe& operator=(const Maybe<U>& rhs); - Maybe& operator=(Maybe&& rhs); + Maybe& operator=(Maybe&& rhs) noexcept; template <typename U> Maybe& operator=(Maybe<U>&& rhs); @@ -134,7 +134,7 @@ Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) { } template <typename T> -Maybe<T>::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) { +Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) { if (!rhs.nothing_) { rhs.nothing_ = true; @@ -192,7 +192,7 @@ Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) { } template <typename T> -inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) { +inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept { // Delegate to the actual assignment. return move(std::forward<Maybe<T>>(rhs)); } |