diff options
38 files changed, 1558 insertions, 362 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 99d8b51e185f..0defc05fc5d0 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24277,7 +24277,9 @@ package android.media.tv { field public static final String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri"; field public static final String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri"; field public static final String COLUMN_APP_LINK_TEXT = "app_link_text"; + field public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre"; field public static final String COLUMN_BROWSABLE = "browsable"; + field public static final String COLUMN_CHANNEL_LIST_ID = "channel_list_id"; field public static final String COLUMN_DESCRIPTION = "description"; field public static final String COLUMN_DISPLAY_NAME = "display_name"; field public static final String COLUMN_DISPLAY_NUMBER = "display_number"; @@ -24292,6 +24294,8 @@ package android.media.tv { field public static final String COLUMN_LOCKED = "locked"; field public static final String COLUMN_NETWORK_AFFILIATION = "network_affiliation"; field public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id"; + field public static final String COLUMN_REMOTE_CONTROL_KEY_PRESET_NUMBER = "remote_control_key_preset_number"; + field public static final String COLUMN_SCRAMBLED = "scrambled"; field public static final String COLUMN_SEARCHABLE = "searchable"; field public static final String COLUMN_SERVICE_ID = "service_id"; field public static final String COLUMN_SERVICE_TYPE = "service_type"; @@ -24300,6 +24304,7 @@ package android.media.tv { field public static final String COLUMN_TYPE = "type"; field public static final String COLUMN_VERSION_NUMBER = "version_number"; field public static final String COLUMN_VIDEO_FORMAT = "video_format"; + field public static final String COLUMN_VIDEO_RESOLUTION = "video_resolution"; field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel"; field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/channel"; field public static final android.net.Uri CONTENT_URI; diff --git a/core/java/android/content/pm/LAUNCHER_OWNERS b/core/java/android/content/pm/LAUNCHER_OWNERS new file mode 100644 index 000000000000..400836f55ceb --- /dev/null +++ b/core/java/android/content/pm/LAUNCHER_OWNERS @@ -0,0 +1,7 @@ +set noparent + +omakoto@google.com +sunnygoyal@google.com +mett@google.com +jonmiranda@google.com +pinyaoting@google.com diff --git a/core/java/android/content/pm/SHORTCUT_OWNERS b/core/java/android/content/pm/SHORTCUT_OWNERS new file mode 100644 index 000000000000..3688d5a3a4c7 --- /dev/null +++ b/core/java/android/content/pm/SHORTCUT_OWNERS @@ -0,0 +1,7 @@ +set noparent + +omakoto@google.com +yamasani@google.com +sunnygoyal@google.com +mett@google.com +pinyaoting@google.com diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index f7fad2c5bbaa..2dd51b4459e7 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -143,6 +143,10 @@ public abstract class KernelCpuUidTimeReader<T> { */ public void removeUid(int uid) { mLastTimes.delete(uid); + + if (mBpfTimesAvailable) { + mBpfReader.removeUidsInRange(uid, uid); + } } /** diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index d601029640dd..e44ab34285a4 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -469,6 +469,8 @@ applications that come with the platform <privapp-permissions package="com.android.traceur"> <!-- Permissions required to receive BUGREPORT_STARTED intent --> <permission name="android.permission.DUMP"/> + <!-- Permissions required to start/stop tracing --> + <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/> <!-- Permissions required for quick settings tile --> <permission name="android.permission.STATUS_BAR"/> </privapp-permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index faf49733050a..145ad1368108 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -49,12 +49,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-2062338592": { - "message": "Looking for task of %s", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, "-2054442123": { "message": "Setting Intent of %s to %s", "level": "VERBOSE", @@ -475,6 +469,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-1559645910": { + "message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s", + "level": "DEBUG", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/RootWindowContainer.java" + }, "-1558137010": { "message": "Config is relaunching invisible activity %s called by %s", "level": "VERBOSE", @@ -1531,6 +1531,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-373110070": { + "message": "Skipping task: (mismatch activity\/task) %s", + "level": "DEBUG", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/RootWindowContainer.java" + }, "-354571697": { "message": "Existence Changed in transition %d: %s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java index 8b9e36a3b6b1..496e4707dbff 100644 --- a/graphics/java/android/graphics/RenderEffect.java +++ b/graphics/java/android/graphics/RenderEffect.java @@ -24,7 +24,11 @@ import libcore.util.NativeAllocationRegistry; /** * Intermediate rendering step used to render drawing commands with a corresponding - * visual effect + * visual effect. A {@link RenderEffect} can be configured on a {@link RenderNode} through + * {@link RenderNode#setRenderEffect(RenderEffect)} and will be applied when drawn through + * {@link Canvas#drawRenderNode(RenderNode)}. + * Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through + * {@link android.view.View#setRenderEffect(RenderEffect)} */ public final class RenderEffect { @@ -156,7 +160,8 @@ public final class RenderEffect { * @param src Optional subset of the bitmap to be part of the rendered output. If null * is provided, the entire bitmap bounds are used. * @param dst Bounds of the destination which the bitmap is translated and scaled to be - * drawn into + * drawn into within the bounds of the {@link RenderNode} this RenderEffect is + * installed on */ @NonNull public static RenderEffect createBitmapEffect( @@ -222,8 +227,8 @@ public final class RenderEffect { * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances * combined by the specified {@link BlendMode} * - * @param dst The Dst pixels used in blending, if null the source bitmap is used. - * @param src The Src pixels used in blending, if null the source bitmap is use + * @param dst The Dst pixels used in blending + * @param src The Src pixels used in blending * @param blendMode The {@link BlendMode} to be used to combine colors from the two * {@link RenderEffect}s */ @@ -246,7 +251,11 @@ public final class RenderEffect { * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are * treated as the source bitmap passed to 'outer', i.e. * - * result = outer(inner(source)). + * <pre> + * {@code + * result = outer(inner(source)) + * } + * </pre> * * Consumers should favor explicit chaining of {@link RenderEffect} instances at creation time * rather than using chain effect. Chain effects are useful for situations where the input or diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 433c6227cd5f..30a14c84b72e 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -2450,6 +2450,71 @@ public final class TvContract { */ public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id"; + /** + * The remote control key preset number that is assigned to this channel. + * + * <p> This can be used for one-touch-tuning, tuning to the channel with + * pressing the preset button. + * + * <p> Type: INTEGER (remote control key preset number) + */ + public static final String COLUMN_REMOTE_CONTROL_KEY_PRESET_NUMBER = + "remote_control_key_preset_number"; + + /** + * The flag indicating whether this TV channel is scrambled or not. + * + * <p>Use the same coding for scrambled in the underlying broadcast standard + * if {@code free_ca_mode} in SDT is defined there (e.g. ETSI EN 300 468). + * + * <p>Type: INTEGER (boolean) + */ + public static final String COLUMN_SCRAMBLED = "scrambled"; + + /** + * The typical video resolution. + * + * <p>This is primarily used to filter out channels based on video resolution + * by applications. The value is from SDT if defined there. (e.g. ETSI EN 300 468) + * The value should match one of the followings: {@link #VIDEO_RESOLUTION_SD}, + * {@link #VIDEO_RESOLUTION_HD}, {@link #VIDEO_RESOLUTION_UHD}. + * + * <p>Type: TEXT + * + */ + public static final String COLUMN_VIDEO_RESOLUTION = "video_resolution"; + + /** + * The channel list ID of this TV channel. + * + * <p>It is used to identify the channel list constructed from broadcast SI based on the + * underlying broadcast standard or country/operator profile, if applicable. Otherwise, + * leave empty. + * + * <p>The ID can be defined by individual TV input services. For example, one may assign a + * service operator name for the service operator channel list constructed from broadcast + * SI or one may assign the {@code profile_name} of the operator_info() APDU defined in CI + * Plus 1.3 for the dedicated CICAM operator profile channel list constructed + * from CICAM NIT. + * + * <p>Type: TEXT + */ + public static final String COLUMN_CHANNEL_LIST_ID = "channel_list_id"; + + /** + * The comma-separated genre string of this TV channel. + * + * <p>Use the same language appeared in the underlying broadcast standard, if applicable. + * Otherwise, leave empty. Use + * {@link Genres#encode Genres.encode()} to create a text that can be stored in this column. + * Use {@link Genres#decode Genres.decode()} to get the broadcast genre strings from the + * text stored in the column. + * + * <p>Type: TEXT + * @see Programs#COLUMN_BROADCAST_GENRE + */ + public static final String COLUMN_BROADCAST_GENRE = Programs.COLUMN_BROADCAST_GENRE; + private Channels() {} /** diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 383416221901..0d4e7460c21f 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -56,6 +56,7 @@ java_defaults { "SettingsLibTopIntroPreference", "SettingsLibBannerMessagePreference", "SettingsLibFooterPreference", + "SettingsLibUsageProgressBarPreference", ], } diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp new file mode 100644 index 000000000000..f346a5962d1c --- /dev/null +++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLibUsageProgressBarPreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.preference_preference", + ], + + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml b/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml new file mode 100644 index 000000000000..51fc7ed64660 --- /dev/null +++ b/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml new file mode 100644 index 000000000000..9dbd5fa4b2d2 --- /dev/null +++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="vertical" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:paddingTop="32dp" + android:paddingBottom="32dp"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/usage_summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_alignBaseline="@id/total_summary" + android:singleLine="true" + android:ellipsize="marquee" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1" + android:textSize="20sp"/> + <TextView + android:id="@+id/total_summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"/> + </RelativeLayout> + + <ProgressBar + android:id="@android:id/progress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scaleY="2" + android:layout_marginTop="4dp" + android:max="100"/> +</LinearLayout> diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java new file mode 100644 index 000000000000..950a8b4ad7cc --- /dev/null +++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 The Android Open Source 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.widget; + +import android.content.Context; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.RelativeSizeSpan; +import android.util.AttributeSet; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Progres bar preference with a usage summary and a total summary. + * This preference shows number in usage summary with enlarged font size. + */ +public class UsageProgressBarPreference extends Preference { + + private final Pattern mNumberPattern = Pattern.compile("[\\d]*\\.?[\\d]+"); + + private CharSequence mUsageSummary; + private CharSequence mTotalSummary; + private int mPercent = -1; + + /** + * Perform inflation from XML and apply a class-specific base style. + * + * @param context The {@link Context} this is associated with, through which it can + * access the current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference + * @param defStyle An attribute in the current theme that contains a reference to a style + * resource that supplies default values for the view. Can be 0 to not + * look for defaults. + */ + public UsageProgressBarPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setLayoutResource(R.layout.preference_usage_progress_bar); + } + + /** + * Perform inflation from XML and apply a class-specific base style. + * + * @param context The {@link Context} this is associated with, through which it can + * access the current theme, resources, {@link SharedPreferences}, etc. + * @param attrs The attributes of the XML tag that is inflating the preference + */ + public UsageProgressBarPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.preference_usage_progress_bar); + } + + /** + * Constructor to create a preference. + * + * @param context The Context this is associated with. + */ + public UsageProgressBarPreference(Context context) { + this(context, null); + } + + /** Set usage summary, number in the summary will show with enlarged font size. */ + public void setUsageSummary(CharSequence usageSummary) { + if (TextUtils.equals(mUsageSummary, usageSummary)) { + return; + } + mUsageSummary = usageSummary; + notifyChanged(); + } + + /** Set total summary. */ + public void setTotalSummary(CharSequence totalSummary) { + if (TextUtils.equals(mTotalSummary, totalSummary)) { + return; + } + mTotalSummary = totalSummary; + notifyChanged(); + } + + /** Set percentage of the progress bar. */ + public void setPercent(long usage, long total) { + if (total == 0L || usage > total) { + return; + } + final int percent = (int) (usage / (double) total * 100); + if (mPercent == percent) { + return; + } + mPercent = percent; + notifyChanged(); + } + + /** + * Binds the created View to the data for this preference. + * + * <p>This is a good place to grab references to custom Views in the layout and set + * properties on them. + * + * <p>Make sure to call through to the superclass's implementation. + * + * @param holder The ViewHolder that provides references to the views to fill in. These views + * will be recycled, so you should not hold a reference to them after this method + * returns. + */ + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + final TextView usageSummary = (TextView) holder.findViewById(R.id.usage_summary); + usageSummary.setText(enlargeFontOfNumber(mUsageSummary)); + + final TextView totalSummary = (TextView) holder.findViewById(R.id.total_summary); + if (mTotalSummary != null) { + totalSummary.setText(mTotalSummary); + } + + final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress); + if (mPercent < 0) { + progressBar.setIndeterminate(true); + } else { + progressBar.setIndeterminate(false); + progressBar.setProgress(mPercent); + } + } + + private CharSequence enlargeFontOfNumber(CharSequence summary) { + if (TextUtils.isEmpty(summary)) { + return ""; + } + + final Matcher matcher = mNumberPattern.matcher(summary); + if (matcher.find()) { + final SpannableString spannableSummary = new SpannableString(summary); + spannableSummary.setSpan(new RelativeSizeSpan(2.4f), matcher.start(), + matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return spannableSummary; + } + return summary; + } +} diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml index 6e611960b2c7..8ba9033ef1fb 100644 --- a/packages/SettingsLib/res/values/styles_support_preference.xml +++ b/packages/SettingsLib/res/values/styles_support_preference.xml @@ -23,7 +23,6 @@ <!-- Footer Preferences --> <style name="Preference.FooterPreference.SettingsBase" parent="@style/Preference.Material"> <item name="android:layout">@layout/preference_footer</item> - <item name="allowDividerAbove">true</item> </style> <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay"> diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index 2ccff1ecaf6c..f6f9dba784a0 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -33,10 +33,12 @@ android_test { test_suites: ["device-tests"], static_libs: [ + "androidx.test.core", "androidx.test.rules", "androidx.test.espresso.core", "mockito-target-minus-junit4", "truth-prebuilt", + "SettingsLibUsageProgressBarPreference", ], dxflags: ["--multi-dex"], diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java new file mode 100644 index 000000000000..85e2174bf87a --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 The Android Open Source 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.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.text.SpannedString; +import android.text.style.RelativeSizeSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import androidx.preference.PreferenceViewHolder; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class UsageProgressBarPreferenceTest { + + private UsageProgressBarPreference mUsageProgressBarPreference; + private PreferenceViewHolder mViewHolder; + + @Before + public void setUp() { + final Context context = ApplicationProvider.getApplicationContext(); + mUsageProgressBarPreference = new UsageProgressBarPreference(context); + final LayoutInflater inflater = LayoutInflater.from(context); + final View rootView = inflater.inflate(mUsageProgressBarPreference.getLayoutResource(), + new LinearLayout(context), false /* attachToRoot */); + mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView); + } + + @Test + public void setUsageSummary_noNumber_noRelativeSizeSpan() { + mUsageProgressBarPreference.setUsageSummary("test"); + + mUsageProgressBarPreference.onBindViewHolder(mViewHolder); + + final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary); + final SpannedString summary = new SpannedString(usageSummary.getText()); + assertThat(summary.getSpans(0, summary.length(), RelativeSizeSpan.class).length) + .isEqualTo(0); + } + + @Test + public void setUsageSummary_integerNumber_findRelativeSizeSpan() { + mUsageProgressBarPreference.setUsageSummary("10Test"); + + mUsageProgressBarPreference.onBindViewHolder(mViewHolder); + + final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary); + final SpannedString summary = new SpannedString(usageSummary.getText()); + assertThat(summary.getSpans(0, summary.length(), RelativeSizeSpan.class).length) + .isEqualTo(1); + } + + @Test + public void setUsageSummary_floatNumber_findRelativeSizeSpan() { + mUsageProgressBarPreference.setUsageSummary("3.14Test"); + + mUsageProgressBarPreference.onBindViewHolder(mViewHolder); + + final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary); + final SpannedString summary = new SpannedString(usageSummary.getText()); + assertThat(summary.getSpans(0, summary.length(), RelativeSizeSpan.class).length) + .isEqualTo(1); + } + + @Test + public void setPercent_getCorrectProgress() { + mUsageProgressBarPreference.setPercent(31, 80); + + mUsageProgressBarPreference.onBindViewHolder(mViewHolder); + + final ProgressBar progressBar = (ProgressBar) mViewHolder + .findViewById(android.R.id.progress); + assertThat(progressBar.getProgress()).isEqualTo((int) (31.0f / 80 * 100)); + } +} diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index db6427970ba1..86af4647ed7a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -839,6 +839,8 @@ <string name="quick_settings_user_new_user">New user</string> <!-- QuickSettings: Wifi [CHAR LIMIT=NONE] --> <string name="quick_settings_wifi_label">Wi-Fi</string> + <!-- QuickSettings: Internet [CHAR LIMIT=NONE] --> + <string name="quick_settings_internet_label">Internet</string> <!-- QuickSettings: Wifi (Not connected) [CHAR LIMIT=NONE] --> <string name="quick_settings_wifi_not_connected">Not Connected</string> <!-- QuickSettings: Wifi (No network) [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 0d0d01249c3d..ac3fd4b51931 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -28,6 +28,7 @@ import android.provider.Settings.Secure; import android.service.quicksettings.Tile; import android.text.TextUtils; import android.util.ArraySet; +import android.util.FeatureFlagUtils; import android.util.Log; import com.android.internal.logging.InstanceId; @@ -444,6 +445,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D final ArrayList<String> tiles = new ArrayList<String>(); boolean addedDefault = false; Set<String> addedSpecs = new ArraySet<>(); + // TODO(b/174753536): Move it into the config file. + if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + tiles.add("internet"); + addedSpecs.add("internet"); + } for (String tile : tileList.split(",")) { tile = tile.trim(); if (tile.isEmpty()) continue; @@ -459,6 +465,12 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D addedDefault = true; } } else { + // TODO(b/174753536): Move it into the config file. + if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { + if (tile.equals("wifi") || tile.equals("cell")) { + continue; + } + } if (!addedSpecs.contains(tile)) { tiles.add(tile); addedSpecs.add(tile); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 24c0fd76d827..e9d481b2e7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -37,6 +37,7 @@ import com.android.systemui.qs.tiles.DataSaverTile; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HotspotTile; +import com.android.systemui.qs.tiles.InternetTile; import com.android.systemui.qs.tiles.LocationTile; import com.android.systemui.qs.tiles.NfcTile; import com.android.systemui.qs.tiles.NightDisplayTile; @@ -60,6 +61,7 @@ public class QSFactoryImpl implements QSFactory { private static final String TAG = "QSFactory"; private final Provider<WifiTile> mWifiTileProvider; + private final Provider<InternetTile> mInternetTileProvider; private final Provider<BluetoothTile> mBluetoothTileProvider; private final Provider<CellularTile> mCellularTileProvider; private final Provider<DndTile> mDndTileProvider; @@ -89,6 +91,7 @@ public class QSFactoryImpl implements QSFactory { Lazy<QSHost> qsHostLazy, Provider<CustomTile.Builder> customTileBuilderProvider, Provider<WifiTile> wifiTileProvider, + Provider<InternetTile> internetTileProvider, Provider<BluetoothTile> bluetoothTileProvider, Provider<CellularTile> cellularTileProvider, Provider<DndTile> dndTileProvider, @@ -113,6 +116,7 @@ public class QSFactoryImpl implements QSFactory { mCustomTileBuilderProvider = customTileBuilderProvider; mWifiTileProvider = wifiTileProvider; + mInternetTileProvider = internetTileProvider; mBluetoothTileProvider = bluetoothTileProvider; mCellularTileProvider = cellularTileProvider; mDndTileProvider = dndTileProvider; @@ -148,6 +152,8 @@ public class QSFactoryImpl implements QSFactory { switch (tileSpec) { case "wifi": return mWifiTileProvider.get(); + case "internet": + return mInternetTileProvider.get(); case "bt": return mBluetoothTileProvider.get(); case "cell": diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java new file mode 100644 index 000000000000..86524f5408c0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2020 The Android Open Source 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.systemui.qs.tiles; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.text.Html; +import android.text.TextUtils; +import android.util.Log; +import android.widget.Switch; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.net.DataUsageController; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSIconView; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.qs.QSTile.Icon; +import com.android.systemui.plugins.qs.QSTile.SignalState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.AlphaControlledSignalTileView; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.WifiIcons; + +import javax.inject.Inject; + +/** Quick settings tile: Internet **/ +public class InternetTile extends QSTileImpl<SignalState> { + private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS); + + protected final NetworkController mController; + private final DataUsageController mDataController; + private final QSTile.SignalState mStateBeforeClick = newTileState(); + // The last updated tile state, 0: mobile, 1: wifi + private int mLastTileState = -1; + + protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback(); + + @Inject + public InternetTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + NetworkController networkController + ) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger); + mController = networkController; + mDataController = mController.getMobileDataController(); + mController.observe(getLifecycle(), mSignalCallback); + } + + @Override + public SignalState newTileState() { + return new SignalState(); + } + + @Override + public QSIconView createTileView(Context context) { + return new AlphaControlledSignalTileView(context); + } + + @Override + public Intent getLongClickIntent() { + return WIFI_SETTINGS; + } + + @Override + protected void handleClick() { + mActivityStarter.postStartActivityDismissingKeyguard(WIFI_SETTINGS, 0); + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_internet_label); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.QS_WIFI; + } + + @Override + public boolean isAvailable() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI) + || (mController.hasMobileDataFeature() + && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM); + } + + private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) { + return isTransient + ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient) + : statusLabel; + } + + private static String removeDoubleQuotes(String string) { + if (string == null) return null; + final int length = string.length(); + if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { + return string.substring(1, length - 1); + } + return string; + } + + private static final class WifiCallbackInfo { + boolean mEnabled; + boolean mConnected; + int mWifiSignalIconId; + String mSsid; + boolean mActivityIn; + boolean mActivityOut; + String mWifiSignalContentDescription; + boolean mIsTransient; + public String mStatusLabel; + + @Override + public String toString() { + return new StringBuilder("WifiCallbackInfo[") + .append("mEnabled=").append(mEnabled) + .append(",mConnected=").append(mConnected) + .append(",mWifiSignalIconId=").append(mWifiSignalIconId) + .append(",mSsid=").append(mSsid) + .append(",mActivityIn=").append(mActivityIn) + .append(",mActivityOut=").append(mActivityOut) + .append(",mWifiSignalContentDescription=").append(mWifiSignalContentDescription) + .append(",mIsTransient=").append(mIsTransient) + .append(']').toString(); + } + } + + private static final class CellularCallbackInfo { + boolean mAirplaneModeEnabled; + CharSequence mDataSubscriptionName; + CharSequence mDataContentDescription; + int mMobileSignalIconId; + boolean mActivityIn; + boolean mActivityOut; + boolean mNoSim; + boolean mRoaming; + boolean mMultipleSubs; + + @Override + public String toString() { + return new StringBuilder("CellularCallbackInfo[") + .append("mAirplaneModeEnabled=").append(mAirplaneModeEnabled) + .append(",mDataSubscriptionName=").append(mDataSubscriptionName) + .append(",mDataContentDescription=").append(mDataContentDescription) + .append(",mMobileSignalIconId=").append(mMobileSignalIconId) + .append(",mActivityIn=").append(mActivityIn) + .append(",mActivityOut=").append(mActivityOut) + .append(",mNoSim=").append(mNoSim) + .append(",mRoaming=").append(mRoaming) + .append(",mMultipleSubs=").append(mMultipleSubs) + .append(']').toString(); + } + } + + protected final class InternetSignalCallback implements SignalCallback { + final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo(); + final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo(); + + @Override + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description, boolean isTransient, + String statusLabel) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "setWifiIndicators: " + + "enabled = " + enabled + "," + + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," + + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + "," + + "activityIn = " + activityIn + "," + + "activityOut = " + activityOut + "," + + "description = " + description + "," + + "isTransient = " + isTransient + "," + + "statusLabel = " + statusLabel); + } + mWifiInfo.mEnabled = enabled; + mWifiInfo.mConnected = qsIcon.visible; + mWifiInfo.mWifiSignalIconId = qsIcon.icon; + mWifiInfo.mSsid = description; + mWifiInfo.mActivityIn = activityIn; + mWifiInfo.mActivityOut = activityOut; + mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription; + mWifiInfo.mIsTransient = isTransient; + mWifiInfo.mStatusLabel = statusLabel; + refreshState(mWifiInfo); + } + + @Override + public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, + int qsType, boolean activityIn, boolean activityOut, + CharSequence typeContentDescription, + CharSequence typeContentDescriptionHtml, CharSequence description, + boolean isWide, int subId, boolean roaming) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "setMobileDataIndicators: " + + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," + + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + "," + + "statusType = " + statusType + "," + + "qsType = " + qsType + "," + + "activityIn = " + activityIn + "," + + "activityOut = " + activityOut + "," + + "typeContentDescription = " + typeContentDescription + "," + + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + "," + + "description = " + description + "," + + "isWide = " + isWide + "," + + "subId = " + subId + "," + + "roaming = " + roaming); + } + if (qsIcon == null) { + // Not data sim, don't display. + return; + } + mCellularInfo.mDataSubscriptionName = mController.getMobileDataNetworkName(); + mCellularInfo.mDataContentDescription = + (description != null) ? typeContentDescriptionHtml : null; + mCellularInfo.mMobileSignalIconId = qsIcon.icon; + mCellularInfo.mActivityIn = activityIn; + mCellularInfo.mActivityOut = activityOut; + mCellularInfo.mRoaming = roaming; + mCellularInfo.mMultipleSubs = mController.getNumberSubscriptions() > 1; + refreshState(mCellularInfo); + } + + @Override + public void setNoSims(boolean show, boolean simDetected) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "setNoSims: " + + "show = " + show + "," + + "simDetected = " + simDetected); + } + mCellularInfo.mNoSim = show; + if (mCellularInfo.mNoSim) { + // Make sure signal gets cleared out when no sims. + mCellularInfo.mMobileSignalIconId = 0; + } + refreshState(mCellularInfo); + } + + @Override + public void setIsAirplaneMode(IconState icon) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "InternetTile-setIsAirplaneMode: " + + "icon = " + (icon == null ? "" : icon.toString())); + } + mCellularInfo.mAirplaneModeEnabled = icon.visible; + refreshState(mCellularInfo); + } + } + + @Override + protected void handleUpdateState(SignalState state, Object arg) { + Log.d(TAG, "handleUpdateState: " + "arg = " + arg); + if (arg instanceof CellularCallbackInfo) { + mLastTileState = 0; + handleUpdateCellularState(state, arg); + } else if (arg instanceof WifiCallbackInfo) { + mLastTileState = 1; + handleUpdateWifiState(state, arg); + } else { + // handleUpdateState will be triggered when user expands the QuickSetting panel with + // arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo + // should be used to refresh the tile. + if (mLastTileState == 0) { + handleUpdateCellularState(state, mSignalCallback.mCellularInfo); + } else if (mLastTileState == 1) { + handleUpdateWifiState(state, mSignalCallback.mWifiInfo); + } + } + } + + private void handleUpdateWifiState(SignalState state, Object arg) { + WifiCallbackInfo cb = (WifiCallbackInfo) arg; + boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null); + boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null); + boolean enabledChanging = state.value != cb.mEnabled; + if (enabledChanging) { + fireToggleStateChanged(cb.mEnabled); + } + if (state.slash == null) { + state.slash = new SlashState(); + state.slash.rotation = 6; + } + state.slash.isSlashed = false; + state.secondaryLabel = getSecondaryLabel(cb.mIsTransient, removeDoubleQuotes(cb.mSsid)); + state.state = Tile.STATE_ACTIVE; + state.dualTarget = true; + state.value = cb.mEnabled; + state.activityIn = cb.mEnabled && cb.mActivityIn; + state.activityOut = cb.mEnabled && cb.mActivityOut; + final StringBuffer minimalContentDescription = new StringBuffer(); + final StringBuffer minimalStateDescription = new StringBuffer(); + final Resources r = mContext.getResources(); + // TODO(b/174753536): Use the new "Internet" string as state.label once available. + if (cb.mIsTransient) { + state.icon = ResourceIcon.get( + com.android.internal.R.drawable.ic_signal_wifi_transient_animation); + state.label = r.getString(R.string.quick_settings_internet_label); + } else if (!state.value) { + state.slash.isSlashed = true; + state.state = Tile.STATE_INACTIVE; + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED); + state.label = r.getString(R.string.quick_settings_internet_label); + } else if (wifiConnected) { + state.icon = ResourceIcon.get(cb.mWifiSignalIconId); + state.label = r.getString(R.string.quick_settings_internet_label); + } else if (wifiNotConnected) { + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); + state.label = r.getString(R.string.quick_settings_internet_label); + } else { + state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK); + state.label = r.getString(R.string.quick_settings_internet_label); + } + minimalContentDescription.append( + mContext.getString(R.string.quick_settings_internet_label)).append(","); + if (state.value) { + if (wifiConnected) { + minimalStateDescription.append(cb.mWifiSignalContentDescription); + minimalContentDescription.append(removeDoubleQuotes(cb.mSsid)); + if (!TextUtils.isEmpty(state.secondaryLabel)) { + minimalContentDescription.append(",").append(state.secondaryLabel); + } + } + } + state.stateDescription = minimalStateDescription.toString(); + state.contentDescription = minimalContentDescription.toString(); + state.dualLabelContentDescription = r.getString( + R.string.accessibility_quick_settings_open_settings, getTileLabel()); + state.expandedAccessibilityClassName = Switch.class.getName(); + } + + private void handleUpdateCellularState(SignalState state, Object arg) { + CellularCallbackInfo cb = (CellularCallbackInfo) arg; + final Resources r = mContext.getResources(); + // TODO(b/174753536): Use the new "Internet" string as state.label once available. + state.label = r.getString(R.string.quick_settings_internet_label); + boolean mobileDataEnabled = mDataController.isMobileDataSupported() + && mDataController.isMobileDataEnabled(); + state.value = mobileDataEnabled; + state.activityIn = mobileDataEnabled && cb.mActivityIn; + state.activityOut = mobileDataEnabled && cb.mActivityOut; + state.expandedAccessibilityClassName = Switch.class.getName(); + if (cb.mNoSim) { + state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim); + } else { + state.icon = new SignalIcon(cb.mMobileSignalIconId); + } + + if (cb.mNoSim) { + state.state = Tile.STATE_UNAVAILABLE; + state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short); + } else if (cb.mAirplaneModeEnabled) { + state.state = Tile.STATE_UNAVAILABLE; + state.secondaryLabel = r.getString(R.string.status_bar_airplane); + } else if (mobileDataEnabled) { + state.state = Tile.STATE_ACTIVE; + state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName, + getMobileDataContentName(cb)); + } else { + state.state = Tile.STATE_INACTIVE; + state.secondaryLabel = r.getString(R.string.cell_data_off); + } + + state.contentDescription = state.label; + if (state.state == Tile.STATE_INACTIVE) { + // This information is appended later by converting the Tile.STATE_INACTIVE state. + state.stateDescription = ""; + } else { + state.stateDescription = state.secondaryLabel; + } + } + + private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) { + if (TextUtils.isEmpty(dataType)) { + return Html.fromHtml((current == null ? "" : current.toString()), 0); + } + if (TextUtils.isEmpty(current)) { + return Html.fromHtml((dataType == null ? "" : dataType.toString()), 0); + } + String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType); + return Html.fromHtml(concat, 0); + } + + private CharSequence getMobileDataContentName(CellularCallbackInfo cb) { + if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) { + String roaming = mContext.getString(R.string.data_connection_roaming); + String dataDescription = + cb.mDataContentDescription == null ? "" + : cb.mDataContentDescription.toString(); + return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription); + } + if (cb.mRoaming) { + return mContext.getString(R.string.data_connection_roaming); + } + return cb.mDataContentDescription; + } + + private static class SignalIcon extends Icon { + private final int mState; + SignalIcon(int state) { + mState = state; + } + public int getState() { + return mState; + } + + @Override + public Drawable getDrawable(Context context) { + SignalDrawable d = new SignalDrawable(context); + d.setLevel(getState()); + return d; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 8722fecdad96..f92860b70116 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -104,6 +104,15 @@ public interface NetworkController extends CallbackController<SignalCallback>, D Context context) { this(visible, icon, context.getString(contentDescription)); } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + return builder.append("[visible=").append(visible).append(',') + .append("icon=").append(icon).append(',') + .append("contentDescription=").append(contentDescription).append(']') + .toString(); + } } /** diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 13787768dc93..04032471ff87 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -95,7 +95,6 @@ import android.net.INetworkManagementEventObserver; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; -import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.ISocketKeepaliveCallback; import android.net.InetAddresses; @@ -337,7 +336,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @VisibleForTesting protected INetd mNetd; private INetworkStatsService mStatsService; - private INetworkPolicyManager mPolicyManager; + private NetworkPolicyManager mPolicyManager; private NetworkPolicyManagerInternal mPolicyManagerInternal; /** @@ -946,15 +945,15 @@ public class ConnectivityService extends IConnectivityManager.Stub } public ConnectivityService(Context context, INetworkManagementService netManager, - INetworkStatsService statsService, INetworkPolicyManager policyManager) { - this(context, netManager, statsService, policyManager, getDnsResolver(context), - new IpConnectivityLog(), NetdService.getInstance(), new Dependencies()); + INetworkStatsService statsService) { + this(context, netManager, statsService, getDnsResolver(context), new IpConnectivityLog(), + NetdService.getInstance(), new Dependencies()); } @VisibleForTesting protected ConnectivityService(Context context, INetworkManagementService netManager, - INetworkStatsService statsService, INetworkPolicyManager policyManager, - IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) { + INetworkStatsService statsService, IDnsResolver dnsresolver, IpConnectivityLog logger, + INetd netd, Dependencies deps) { if (DBG) log("ConnectivityService starting up"); mDeps = Objects.requireNonNull(deps, "missing Dependencies"); @@ -992,7 +991,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService"); mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService"); - mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager"); + mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); mPolicyManagerInternal = Objects.requireNonNull( LocalServices.getService(NetworkPolicyManagerInternal.class), "missing NetworkPolicyManagerInternal"); @@ -1008,12 +1007,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // To ensure uid rules are synchronized with Network Policy, register for // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService // reading existing policy from disk. - try { - mPolicyManager.registerListener(mPolicyListener); - } catch (RemoteException e) { - // ouch, no rules updates means some processes may never get network - loge("unable to register INetworkPolicyListener" + e); - } + mPolicyManager.registerListener(mPolicyListener); final PowerManager powerManager = (PowerManager) context.getSystemService( Context.POWER_SERVICE); diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java index 2bc8925be019..f701688b2b7e 100644 --- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java +++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java @@ -20,7 +20,6 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import android.content.Context; -import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.os.INetworkManagementService; import android.os.ServiceManager; @@ -38,7 +37,7 @@ public final class ConnectivityServiceInitializer extends SystemService { super(context); // TODO: Define formal APIs to get the needed services. mConnectivity = new ConnectivityService(context, getNetworkManagementService(), - getNetworkStatsService(), getNetworkPolicyManager()); + getNetworkStatsService()); } @Override @@ -57,10 +56,4 @@ public final class ConnectivityServiceInitializer extends SystemService { return INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); } - - private INetworkPolicyManager getNetworkPolicyManager() { - return INetworkPolicyManager.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); - } - } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5fde046bda0a..6a52266dfe40 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1700,12 +1700,13 @@ public final class ActiveServices { // TODO: remove as part of fixing b/173627642 @SuppressWarnings("AndroidFrameworkCompatChange") private void postFgsNotificationLocked(ServiceRecord r) { + final boolean isLegacyApp = (r.appInfo.targetSdkVersion < Build.VERSION_CODES.S); boolean showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled; if (!showNow) { // Legacy apps' FGS notifications are not deferred unless the relevant // DeviceConfig element has been set showNow = mAm.mConstants.mFlagFgsNotificationDeferralApiGated - && r.appInfo.targetSdkVersion < Build.VERSION_CODES.S; + && isLegacyApp; } if (!showNow) { // is the notification such that it should show right away? @@ -1760,6 +1761,11 @@ public final class ActiveServices { Slog.d(TAG_SERVICE, "FGS " + r + " notification in " + (when - now) + " ms"); } + if (isLegacyApp) { + Slog.i(TAG_SERVICE, "Deferring FGS notification in legacy app " + + r.appInfo.packageName + "/" + UserHandle.formatUid(r.appInfo.uid) + + " : " + r.foregroundNoti); + } mAm.mHandler.postAtTime(mPostDeferredFGSNotifications, when); } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index b0f296f68a47..6892ef770901 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -370,9 +370,9 @@ final class ActivityManagerConstants extends ContentObserver { volatile boolean mFlagFgsNotificationDeferralEnabled = true; // Restrict FGS notification deferral policy to only those apps that target - // API version S or higher. Enabled by default; set to "false" to defer FGS - // notifications from legacy apps as well. - volatile boolean mFlagFgsNotificationDeferralApiGated = true; + // API version S or higher. Disabled by default; set to "true" to force + // legacy app FGS notifications to display immediately in all cases. + volatile boolean mFlagFgsNotificationDeferralApiGated = false; // Time in milliseconds to defer FGS notifications after their transition to // the foreground state. @@ -806,7 +806,7 @@ final class ActivityManagerConstants extends ContentObserver { mFlagFgsNotificationDeferralApiGated = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_DEFERRED_FGS_NOTIFICATIONS_API_GATED, - /*default value*/ true); + /*default value*/ false); } private void updateFgsNotificationDeferralInterval() { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index cd5ea46afb8d..61da564ebab0 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2664,14 +2664,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { + ainfo.applicationInfo.uid + ", calling uid=" + callingUid); } - final Task stack = r.getRootTask(); - final Task task = stack.getDisplayArea().createRootTask(stack.getWindowingMode(), - stack.getActivityType(), !ON_TOP, ainfo, intent, - false /* createdByOrganizer */); + final Task rootTask = r.getRootTask(); + final Task task = new Task.Builder(this) + .setWindowingMode(rootTask.getWindowingMode()) + .setActivityType(rootTask.getActivityType()) + .setActivityInfo(ainfo) + .setParent(rootTask.getDisplayArea()) + .setIntent(intent) + .build(); if (!mRecentTasks.addToBottom(task)) { // The app has too many tasks already and we can't add any more - stack.removeChild(task, "addAppTask"); + rootTask.removeChild(task, "addAppTask"); return INVALID_TASK_ID; } task.getTaskDescription().copyFrom(description); diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 36a1ef9f49b4..0fc4cdb6a928 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -547,19 +547,16 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED; } - public boolean hasCompatibleActivityType(ConfigurationContainer other) { - /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType(); - /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType(); - - if (thisType == otherType) { + public static boolean isCompatibleActivityType(int currentType, int otherType) { + if (currentType == otherType) { return true; } - if (thisType == ACTIVITY_TYPE_ASSISTANT) { + if (currentType == ACTIVITY_TYPE_ASSISTANT) { // Assistant activities are only compatible with themselves... return false; } // Otherwise we are compatible if us or other is not currently defined. - return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED; + return currentType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED; } /** diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 5195edfb7c86..54996a66ecd7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -306,52 +306,54 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); static class FindTaskResult implements Function<Task, Boolean> { - ActivityRecord mRecord; - boolean mIdealMatch; + ActivityRecord mIdealRecord; + ActivityRecord mCandidateRecord; - private ActivityRecord mTarget; - private Intent intent; - private ActivityInfo info; + private int mActivityType; + private String mTaskAffinity; + private Intent mIntent; + private ActivityInfo mInfo; private ComponentName cls; private int userId; private boolean isDocument; private Uri documentData; + void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) { + mActivityType = activityType; + mTaskAffinity = taskAffinity; + mIntent = intent; + mInfo = info; + mIdealRecord = null; + mCandidateRecord = null; + } + /** * Returns the top activity in any existing task matching the given Intent in the input * result. Returns null if no such task is found. */ - void process(ActivityRecord target, Task parent) { - mTarget = target; - - intent = target.intent; - info = target.info; - cls = intent.getComponent(); - if (info.targetActivity != null) { - cls = new ComponentName(info.packageName, info.targetActivity); + void process(WindowContainer parent) { + cls = mIntent.getComponent(); + if (mInfo.targetActivity != null) { + cls = new ComponentName(mInfo.packageName, mInfo.targetActivity); } - userId = UserHandle.getUserId(info.applicationInfo.uid); - isDocument = intent != null & intent.isDocument(); + userId = UserHandle.getUserId(mInfo.applicationInfo.uid); + isDocument = mIntent != null & mIntent.isDocument(); // If documentData is non-null then it must match the existing task data. - documentData = isDocument ? intent.getData() : null; + documentData = isDocument ? mIntent.getData() : null; - ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", target, + ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", mInfo, parent); parent.forAllLeafTasks(this); } - void clear() { - mRecord = null; - mIdealMatch = false; - } - - void setTo(FindTaskResult result) { - mRecord = result.mRecord; - mIdealMatch = result.mIdealMatch; - } - @Override public Boolean apply(Task task) { + if (!ConfigurationContainer.isCompatibleActivityType(mActivityType, + task.getActivityType())) { + ProtoLog.d(WM_DEBUG_TASKS, "Skipping task: (mismatch activity/task) %s", task); + return false; + } + if (task.voiceSession != null) { // We never match voice sessions; those always run independently. ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: voice session", task); @@ -370,7 +372,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r); return false; } - if (!r.hasCompatibleActivityType(mTarget)) { + if (!ConfigurationContainer.isCompatibleActivityType(r.getActivityType(), + mActivityType)) { ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch activity type", task); return false; } @@ -391,35 +394,33 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } ProtoLog.d(WM_DEBUG_TASKS, "Comparing existing cls=%s /aff=%s to new cls=%s /aff=%s", - r.getTask().rootAffinity, intent.getComponent().flattenToShortString(), - info.taskAffinity, (task.realActivity != null + r.getTask().rootAffinity, mIntent.getComponent().flattenToShortString(), + mInfo.taskAffinity, (task.realActivity != null ? task.realActivity.flattenToShortString() : "")); // TODO Refactor to remove duplications. Check if logic can be simplified. if (task.realActivity != null && task.realActivity.compareTo(cls) == 0 && Objects.equals(documentData, taskDocumentData)) { ProtoLog.d(WM_DEBUG_TASKS, "Found matching class!"); //dump(); - ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", intent, r.intent); - mRecord = r; - mIdealMatch = true; + ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", mIntent, r.intent); + mIdealRecord = r; return true; } else if (affinityIntent != null && affinityIntent.getComponent() != null && affinityIntent.getComponent().compareTo(cls) == 0 && Objects.equals(documentData, taskDocumentData)) { ProtoLog.d(WM_DEBUG_TASKS, "Found matching class!"); - ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", intent, r.intent); - mRecord = r; - mIdealMatch = true; + ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", mIntent, r.intent); + mIdealRecord = r; return true; } else if (!isDocument && !taskIsDocument - && mRecord == null && task.rootAffinity != null) { - if (task.rootAffinity.equals(mTarget.taskAffinity)) { + && mIdealRecord == null && mCandidateRecord == null + && task.rootAffinity != null) { + if (task.rootAffinity.equals(mTaskAffinity)) { ProtoLog.d(WM_DEBUG_TASKS, "Found matching affinity candidate!"); // It is possible for multiple tasks to have the same root affinity especially // if they are in separate stacks. We save off this candidate, but keep looking // to see if there is a better candidate. - mRecord = r; - mIdealMatch = false; + mCandidateRecord = r; } } else { ProtoLog.d(WM_DEBUG_TASKS, "Not a match: %s", task); @@ -2152,9 +2153,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } else { // In the case of multiple activities, we will create a new task for it and then // move the PIP activity into the task. - rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_UNDEFINED, - r.getActivityType(), ON_TOP, r.info, r.intent, - false /* createdByOrganizer */); + rootTask = new Task.Builder(mService) + .setActivityType(r.getActivityType()) + .setOnTop(true) + .setActivityInfo(r.info) + .setParent(taskDisplayArea) + .setIntent(r.intent) + .build(); // It's possible the task entering PIP is in freeform, so save the last // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore // to its previous freeform bounds. @@ -2234,38 +2239,48 @@ class RootWindowContainer extends WindowContainer<DisplayContent> @Nullable ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) { - ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s", r); - mTmpFindTaskResult.clear(); + return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info, + preferredTaskDisplayArea); + } + + @Nullable + ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info, + TaskDisplayArea preferredTaskDisplayArea) { + ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s" + + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info, + preferredTaskDisplayArea); + mTmpFindTaskResult.init(activityType, taskAffinity, intent, info); // Looking up task on preferred display area first + ActivityRecord candidateActivity = null; if (preferredTaskDisplayArea != null) { - preferredTaskDisplayArea.findTaskLocked(r, true /* isPreferredDisplay */, - mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; + mTmpFindTaskResult.process(preferredTaskDisplayArea); + if (mTmpFindTaskResult.mIdealRecord != null) { + return mTmpFindTaskResult.mIdealRecord; + } else if (mTmpFindTaskResult.mCandidateRecord != null) { + candidateActivity = mTmpFindTaskResult.mCandidateRecord; } } - final ActivityRecord task = getItemFromTaskDisplayAreas(taskDisplayArea -> { + final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> { if (taskDisplayArea == preferredTaskDisplayArea) { return null; } - taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */, - mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; + mTmpFindTaskResult.process(taskDisplayArea); + if (mTmpFindTaskResult.mIdealRecord != null) { + return mTmpFindTaskResult.mIdealRecord; } return null; }); - if (task != null) { - return task; + if (idealMatchActivity != null) { + return idealMatchActivity; } - if (WM_DEBUG_TASKS.isEnabled() && mTmpFindTaskResult.mRecord == null) { + if (WM_DEBUG_TASKS.isEnabled() && candidateActivity == null) { ProtoLog.d(WM_DEBUG_TASKS, "No task found"); } - return mTmpFindTaskResult.mRecord; + return candidateActivity; } /** diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 81cbd61d62d7..a0e6b423f299 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -807,55 +807,20 @@ class Task extends WindowContainer<WindowContainer> { // Tracking cookie for the creation of this task. IBinder mLaunchCookie; - /** - * Don't use constructor directly. Use {@link TaskDisplayArea#createStackUnchecked()} instead. - */ - Task(ActivityTaskManagerService atmService, int id, int activityType, ActivityInfo info, - Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, - IBinder launchCookie) { - this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/, - null /*taskDescription*/, null /*stack*/); - mCreatedByOrganizer = createdByOrganizer; - mLaunchCookie = launchCookie; - mDeferTaskAppear = deferTaskAppear; - setActivityType(activityType); - } - - /** - * Don't use constructor directly. Use {@link Task#reuseOrCreateTask()} instead. - */ - Task(ActivityTaskManagerService atmService, int _taskId, ActivityInfo info, Intent _intent, - IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, - TaskDescription _taskDescription, Task stack) { - this(atmService, _taskId, _intent, null /*_affinityIntent*/, null /*_affinity*/, - null /*_rootAffinity*/, null /*_realActivity*/, null /*_origActivity*/, - false /*_rootWasReset*/, false /*_autoRemoveRecents*/, false /*_askedCompatMode*/, - UserHandle.getUserId(info.applicationInfo.uid), 0 /*_effectiveUid*/, - null /*_lastDescription*/, System.currentTimeMillis(), - true /*neverRelinquishIdentity*/, - _taskDescription != null ? _taskDescription : new TaskDescription(), - _taskId, INVALID_TASK_ID, INVALID_TASK_ID, - info.applicationInfo.uid, info.packageName, null /* default featureId */, - info.resizeMode, info.supportsPictureInPicture(), false /*_realActivitySuspended*/, - false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info, - _voiceSession, _voiceInteractor, stack); - } - - /** Don't use constructor directly. This is only used by XML parser. */ - Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent, - String _affinity, String _rootAffinity, ComponentName _realActivity, - ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, - boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription, - long lastTimeMoved, boolean neverRelinquishIdentity, + private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, + Intent _affinityIntent, String _affinity, String _rootAffinity, + ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, + boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid, + String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid, String callingPackage, @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info, IVoiceInteractionSession _voiceSession, - IVoiceInteractor _voiceInteractor, Task stack) { + IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, + IBinder _launchCookie, boolean _deferTaskAppear) { super(atmService.mWindowManager); - EventLogTags.writeWmTaskCreated(_taskId, stack != null ? getRootTaskId() : INVALID_TASK_ID); mAtmService = atmService; mTaskSupervisor = atmService.mTaskSupervisor; mRootWindowContainer = mAtmService.mRootWindowContainer; @@ -903,6 +868,11 @@ class Task extends WindowContainer<WindowContainer> { mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity); mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper); mCurrentUser = mAtmService.mAmInternal.getCurrentUserId(); + + mCreatedByOrganizer = _createdByOrganizer; + mLaunchCookie = _launchCookie; + mDeferTaskAppear = _deferTaskAppear; + EventLogTags.writeWmTaskCreated(mTaskId, isRootTask() ? INVALID_TASK_ID : getRootTaskId()); } Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, @@ -4831,14 +4801,36 @@ class Task extends WindowContainer<WindowContainer> { } } - final Task task = new Task(taskSupervisor.mService, taskId, intent, - affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset, - autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription, - lastTimeOnTop, neverRelinquishIdentity, taskDescription, taskAffiliation, - prevTaskId, nextTaskId, callingUid, callingPackage, - callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended, - userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/, - null /*_voiceSession*/, null /*_voiceInteractor*/, null /* stack */); + final Task task = new Task.Builder(taskSupervisor.mService) + .setTaskId(taskId) + .setIntent(intent) + .setAffinityIntent(affinityIntent) + .setAffinity(affinity) + .setRootAffinity(rootAffinity) + .setRealActivity(realActivity) + .setOrigActivity(origActivity) + .setRootWasReset(rootHasReset) + .setAutoRemoveRecents(autoRemoveRecents) + .setAskedCompatMode(askedCompatMode) + .setUserId(userId) + .setEffectiveUid(effectiveUid) + .setLastDescription(lastDescription) + .setLastTimeMoved(lastTimeOnTop) + .setNeverRelinquishIdentity(neverRelinquishIdentity) + .setLastTaskDescription(taskDescription) + .setTaskAffiliation(taskAffiliation) + .setPrevAffiliateTaskId(prevTaskId) + .setNextAffiliateTaskId(nextTaskId) + .setCallingUid(callingUid) + .setCallingPackage(callingPackage) + .setCallingFeatureId(callingFeatureId) + .setResizeMode(resizeMode) + .setSupportsPictureInPicture(supportsPictureInPicture) + .setRealActivitySuspended(realActivitySuspended) + .setUserSetupComplete(userSetupComplete) + .setMinWidth(minWidth) + .setMinHeight(minHeight) + .buildInner(); task.mLastNonFullscreenBounds = lastNonFullscreenBounds; task.setBounds(lastNonFullscreenBounds); task.mWindowLayoutAffinity = windowLayoutAffinity; @@ -7313,11 +7305,15 @@ class Task extends WindowContainer<WindowContainer> { final int taskId = activity != null ? mTaskSupervisor.getNextTaskIdForUser(activity.mUserId) : mTaskSupervisor.getNextTaskIdForUser(); - task = new Task(mAtmService, taskId, info, intent, voiceSession, - voiceInteractor, null /* taskDescription */, this); - - // add the task to stack first, mTaskPositioner might need the stack association - addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0); + task = new Task.Builder(mAtmService) + .setTaskId(taskId) + .setActivityInfo(info) + .setIntent(intent) + .setVoiceSession(voiceSession) + .setVoiceInteractor(voiceInteractor) + .setOnTop(toTop) + .setParent(this) + .build(); } int displayId = getDisplayId(); @@ -7718,4 +7714,368 @@ class Task extends WindowContainer<WindowContainer> { proto.end(token); } + + static class Builder { + private final ActivityTaskManagerService mAtmService; + private WindowContainer mParent; + private int mTaskId; + private Intent mIntent; + private Intent mAffinityIntent; + private String mAffinity; + private String mRootAffinity; + private ComponentName mRealActivity; + private ComponentName mOrigActivity; + private boolean mRootWasReset; + private boolean mAutoRemoveRecents; + private boolean mAskedCompatMode; + private int mUserId; + private int mEffectiveUid; + private String mLastDescription; + private long mLastTimeMoved; + private boolean mNeverRelinquishIdentity; + private TaskDescription mLastTaskDescription; + private int mTaskAffiliation; + private int mPrevAffiliateTaskId = INVALID_TASK_ID; + private int mNextAffiliateTaskId = INVALID_TASK_ID; + private int mCallingUid; + private String mCallingPackage; + private String mCallingFeatureId; + private int mResizeMode; + private boolean mSupportsPictureInPicture; + private boolean mRealActivitySuspended; + private boolean mUserSetupComplete; + private int mMinWidth = INVALID_MIN_SIZE; + private int mMinHeight = INVALID_MIN_SIZE; + private ActivityInfo mActivityInfo; + private IVoiceInteractionSession mVoiceSession; + private IVoiceInteractor mVoiceInteractor; + private int mActivityType; + private int mWindowingMode = WINDOWING_MODE_UNDEFINED; + private boolean mCreatedByOrganizer; + private boolean mDeferTaskAppear; + private IBinder mLaunchCookie; + private boolean mOnTop; + + Builder(ActivityTaskManagerService atm) { + mAtmService = atm; + } + + Builder setParent(WindowContainer parent) { + mParent = parent; + return this; + } + + Builder setTaskId(int taskId) { + mTaskId = taskId; + return this; + } + + Builder setIntent(Intent intent) { + mIntent = intent; + return this; + } + + Builder setRealActivity(ComponentName realActivity) { + mRealActivity = realActivity; + return this; + } + + Builder setEffectiveUid(int effectiveUid) { + mEffectiveUid = effectiveUid; + return this; + } + + Builder setMinWidth(int minWidth) { + mMinWidth = minWidth; + return this; + } + + Builder setMinHeight(int minHeight) { + mMinHeight = minHeight; + return this; + } + + Builder setActivityInfo(ActivityInfo info) { + mActivityInfo = info; + return this; + } + + Builder setVoiceSession(IVoiceInteractionSession voiceSession) { + mVoiceSession = voiceSession; + return this; + } + + Builder setActivityType(int activityType) { + mActivityType = activityType; + return this; + } + + int getActivityType() { + return mActivityType; + } + + Builder setWindowingMode(int windowingMode) { + mWindowingMode = windowingMode; + return this; + } + + int getWindowingMode() { + return mWindowingMode; + } + + Builder setCreatedByOrganizer(boolean createdByOrganizer) { + mCreatedByOrganizer = createdByOrganizer; + return this; + } + + boolean getCreatedByOrganizer() { + return mCreatedByOrganizer; + } + + Builder setDeferTaskAppear(boolean defer) { + mDeferTaskAppear = defer; + return this; + } + + Builder setLaunchCookie(IBinder launchCookie) { + mLaunchCookie = launchCookie; + return this; + } + + Builder setOnTop(boolean onTop) { + mOnTop = onTop; + return this; + } + + private Builder setUserId(int userId) { + mUserId = userId; + return this; + } + + private Builder setLastTimeMoved(long lastTimeMoved) { + mLastTimeMoved = lastTimeMoved; + return this; + } + + private Builder setNeverRelinquishIdentity(boolean neverRelinquishIdentity) { + mNeverRelinquishIdentity = neverRelinquishIdentity; + return this; + } + + private Builder setCallingUid(int callingUid) { + mCallingUid = callingUid; + return this; + } + + private Builder setCallingPackage(String callingPackage) { + mCallingPackage = callingPackage; + return this; + } + + private Builder setResizeMode(int resizeMode) { + mResizeMode = resizeMode; + return this; + } + + private Builder setSupportsPictureInPicture(boolean supportsPictureInPicture) { + mSupportsPictureInPicture = supportsPictureInPicture; + return this; + } + + private Builder setUserSetupComplete(boolean userSetupComplete) { + mUserSetupComplete = userSetupComplete; + return this; + } + + private Builder setTaskAffiliation(int taskAffiliation) { + mTaskAffiliation = taskAffiliation; + return this; + } + + private Builder setPrevAffiliateTaskId(int prevAffiliateTaskId) { + mPrevAffiliateTaskId = prevAffiliateTaskId; + return this; + } + + private Builder setNextAffiliateTaskId(int nextAffiliateTaskId) { + mNextAffiliateTaskId = nextAffiliateTaskId; + return this; + } + + private Builder setCallingFeatureId(String callingFeatureId) { + mCallingFeatureId = callingFeatureId; + return this; + } + + private Builder setRealActivitySuspended(boolean realActivitySuspended) { + mRealActivitySuspended = realActivitySuspended; + return this; + } + + private Builder setLastDescription(String lastDescription) { + mLastDescription = lastDescription; + return this; + } + + private Builder setLastTaskDescription(TaskDescription lastTaskDescription) { + mLastTaskDescription = lastTaskDescription; + return this; + } + + private Builder setOrigActivity(ComponentName origActivity) { + mOrigActivity = origActivity; + return this; + } + + private Builder setRootWasReset(boolean rootWasReset) { + mRootWasReset = rootWasReset; + return this; + } + + private Builder setAutoRemoveRecents(boolean autoRemoveRecents) { + mAutoRemoveRecents = autoRemoveRecents; + return this; + } + + private Builder setAskedCompatMode(boolean askedCompatMode) { + mAskedCompatMode = askedCompatMode; + return this; + } + + private Builder setAffinityIntent(Intent affinityIntent) { + mAffinityIntent = affinityIntent; + return this; + } + + private Builder setAffinity(String affinity) { + mAffinity = affinity; + return this; + } + + private Builder setRootAffinity(String rootAffinity) { + mRootAffinity = rootAffinity; + return this; + } + + private Builder setVoiceInteractor(IVoiceInteractor voiceInteractor) { + mVoiceInteractor = voiceInteractor; + return this; + } + + private void validateRootTask(TaskDisplayArea tda) { + if (mActivityType == ACTIVITY_TYPE_UNDEFINED && !mCreatedByOrganizer) { + // Can't have an undefined root task type yet...so re-map to standard. Anyone + // that wants anything else should be passing it in anyways...except for the task + // organizer. + mActivityType = ACTIVITY_TYPE_STANDARD; + } + + if (mActivityType != ACTIVITY_TYPE_STANDARD + && mActivityType != ACTIVITY_TYPE_UNDEFINED) { + // For now there can be only one root task of a particular non-standard activity + // type on a display. So, get that ignoring whatever windowing mode it is + // currently in. + Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType); + if (rootTask != null) { + throw new IllegalArgumentException("Root task=" + rootTask + " of activityType=" + + mActivityType + " already on display=" + tda + + ". Can't have multiple."); + } + } + + if (!TaskDisplayArea.isWindowingModeSupported(mWindowingMode, + mAtmService.mSupportsMultiWindow, + mAtmService.mSupportsSplitScreenMultiWindow, + mAtmService.mSupportsFreeformWindowManagement, + mAtmService.mSupportsPictureInPicture, mActivityType)) { + throw new IllegalArgumentException("Can't create root task for unsupported " + + "windowingMode=" + mWindowingMode); + } + + if (mWindowingMode == WINDOWING_MODE_PINNED + && mActivityType != ACTIVITY_TYPE_STANDARD) { + throw new IllegalArgumentException( + "Root task with pinned windowing mode cannot with " + + "non-standard activity type."); + } + + if (mWindowingMode == WINDOWING_MODE_PINNED && tda.getRootPinnedTask() != null) { + // Only 1 root task can be PINNED at a time, so dismiss the existing one + tda.getRootPinnedTask().dismissPip(); + } + + // Task created by organizer are added as root. + final Task launchRootTask = mCreatedByOrganizer + ? null : tda.updateLaunchRootTask(mWindowingMode); + if (launchRootTask != null) { + // Since this task will be put into a root task, its windowingMode will be + // inherited. + mWindowingMode = WINDOWING_MODE_UNDEFINED; + mParent = launchRootTask; + } + + mTaskId = tda.getNextRootTaskId(); + } + + Task build() { + if (mParent != null && mParent instanceof TaskDisplayArea) { + validateRootTask((TaskDisplayArea) mParent); + } + + if (mActivityInfo == null) { + mActivityInfo = new ActivityInfo(); + mActivityInfo.applicationInfo = new ApplicationInfo(); + } + + mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid); + mTaskAffiliation = mTaskId; + mLastTimeMoved = System.currentTimeMillis(); + mNeverRelinquishIdentity = true; + mCallingUid = mActivityInfo.applicationInfo.uid; + mCallingPackage = mActivityInfo.packageName; + mResizeMode = mActivityInfo.resizeMode; + mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture(); + if (mLastTaskDescription == null) { + mLastTaskDescription = new TaskDescription(); + } + + final Task task = buildInner(); + + // Set activity type before adding the root task to TaskDisplayArea, so home task can + // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded(). + if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { + task.setActivityType(mActivityType); + } + + if (mParent != null) { + if (mParent instanceof Task) { + final Task parentTask = (Task) mParent; + parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM, + (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0); + } else { + mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM); + } + } + + // Set windowing mode after attached to display area or it abort silently. + if (mWindowingMode != WINDOWING_MODE_UNDEFINED) { + task.setWindowingMode(mWindowingMode, true /* creating */); + } + return task; + } + + /** Don't use {@link Builder#buildInner()} directly. This is only used by XML parser. */ + @VisibleForTesting + Task buildInner() { + return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity, + mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents, + mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved, + mNeverRelinquishIdentity, mLastTaskDescription, mTaskAffiliation, + mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage, + mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, + mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight, + mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer, + mLaunchCookie, mDeferTaskAppear); + } + } } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 97383e3d1386..3f4150b21178 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -35,7 +35,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK; import static com.android.server.wm.DisplayContent.alwaysCreateRootTask; import static com.android.server.wm.Task.ActivityState.RESUMED; @@ -47,9 +46,6 @@ import android.annotation.Nullable; import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.os.IBinder; import android.os.UserHandle; import android.util.IntArray; import android.util.Slog; @@ -131,9 +127,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { */ Task mPreferredTopFocusableRootTask; - private final RootWindowContainer.FindTaskResult - mTmpFindTaskResult = new RootWindowContainer.FindTaskResult(); - /** * If this is the same as {@link #getFocusedRootTask} then the activity on the top of the * focused root task has been resumed. If root tasks are changing position this will hold the @@ -1045,8 +1038,13 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } return stack; } - return createRootTask(windowingMode, activityType, onTop, null /*info*/, intent, - false /* createdByOrganizer */); + return new Task.Builder(mAtmService) + .setWindowingMode(windowingMode) + .setActivityType(activityType) + .setOnTop(onTop) + .setParent(this) + .setIntent(intent) + .build(); } /** @@ -1074,79 +1072,32 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { return mAtmService.mTaskSupervisor.getNextTaskIdForUser(); } - Task createRootTask(int windowingMode, int activityType, boolean onTop) { - return createRootTask(windowingMode, activityType, onTop, null /* info */, - null /* intent */, false /* createdByOrganizer */); - } - - Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info, - Intent intent, boolean createdByOrganizer) { - return createRootTask(windowingMode, activityType, onTop, null /* info */, - null /* intent */, false /* createdByOrganizer */, false /* deferTaskAppear */, - null /* launchCookie */); - } - /** - * Creates a stack matching the input windowing mode and activity type on this display. + * A convinenit method of creating a root task by providing windowing mode and activity type + * on this display. * - * @param windowingMode The windowing mode the stack should be created in. If - * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack - * will - * inherit its parent's windowing mode. - * @param activityType The activityType the stack should be created in. If - * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack - * will - * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. - * @param onTop If true the stack will be created at the top of the display, else - * at the bottom. - * @param info The started activity info. - * @param intent The intent that started this task. - * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false} - * otherwise. - * @param deferTaskAppear @{code true} if the task appeared signal should be deferred. - * @param launchCookie Launch cookie used for tracking/association of the task we are - * creating. - * @return The newly created stack. + * @param windowingMode The windowing mode the root task should be created in. If + * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the + * root task will inherit its parent's windowing mode. + * @param activityType The activityType the root task should be created in. If + * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the + * root task will be created in + * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. + * @param onTop If true the root task will be created at the top of the display, + * else at the bottom. + * @return The newly created root task. */ - Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info, - Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, - IBinder launchCookie) { - if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) { - // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants - // anything else should be passing it in anyways...except for the task organizer. - activityType = ACTIVITY_TYPE_STANDARD; - } - - if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) { - // For now there can be only one stack of a particular non-standard activity type on a - // display. So, get that ignoring whatever windowing mode it is currently in. - Task stack = getRootTask(WINDOWING_MODE_UNDEFINED, activityType); - if (stack != null) { - throw new IllegalArgumentException("Stack=" + stack + " of activityType=" - + activityType + " already on display=" + this + ". Can't have multiple."); - } - } - - if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow, - mAtmService.mSupportsSplitScreenMultiWindow, - mAtmService.mSupportsFreeformWindowManagement, - mAtmService.mSupportsPictureInPicture, activityType)) { - throw new IllegalArgumentException("Can't create stack for unsupported windowingMode=" - + windowingMode); - } - - if (windowingMode == WINDOWING_MODE_PINNED && getRootPinnedTask() != null) { - // Only 1 stack can be PINNED at a time, so dismiss the existing one - getRootPinnedTask().dismissPip(); - } - - final int stackId = getNextRootTaskId(); - return createRootTaskUnchecked(windowingMode, activityType, stackId, onTop, info, intent, - createdByOrganizer, deferTaskAppear, launchCookie); + Task createRootTask(int windowingMode, int activityType, boolean onTop) { + return new Task.Builder(mAtmService) + .setWindowingMode(windowingMode) + .setActivityType(activityType) + .setParent(this) + .setOnTop(onTop) + .build(); } /** @return the root task to create the next task in. */ - private Task updateLaunchRootTask(int windowingMode) { + Task updateLaunchRootTask(int windowingMode) { if (!isSplitScreenWindowingMode(windowingMode)) { // Only split-screen windowing modes can do this currently... return null; @@ -1181,40 +1132,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { return mLaunchRootTask; } - @VisibleForTesting - Task createRootTaskUnchecked(int windowingMode, int activityType, int stackId, boolean onTop, - ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, - IBinder launchCookie) { - if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) { - throw new IllegalArgumentException("Stack with windowing mode cannot with non standard " - + "activity type."); - } - if (info == null) { - info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - } - - // Task created by organizer are added as root. - Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode); - if (launchRootTask != null) { - // Since this stack will be put into a root task, its windowingMode will be inherited. - windowingMode = WINDOWING_MODE_UNDEFINED; - } - - final Task stack = new Task(mAtmService, stackId, activityType, - info, intent, createdByOrganizer, deferTaskAppear, launchCookie); - if (launchRootTask != null) { - launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); - if (onTop) { - positionChildAt(POSITION_TOP, launchRootTask, false /* includingParents */); - } - } else { - addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); - stack.setWindowingMode(windowingMode, true /* creating */); - } - return stack; - } - /** * Get the preferred focusable root task in priority. If the preferred root task does not exist, * find a focusable and visible root task from the top of root tasks in this display. @@ -1391,43 +1308,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { return someActivityPaused[0] > 0; } - /** - * Find task for putting the Activity in. - */ - void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplayArea, - RootWindowContainer.FindTaskResult result) { - mTmpFindTaskResult.clear(); - for (int i = mChildren.size() - 1; i >= 0; --i) { - final Task rootTask = mChildren.get(i).asTask(); - if (rootTask == null) { - continue; - } - if (!r.hasCompatibleActivityType(rootTask) && rootTask.isLeafTask()) { - ProtoLog.d(WM_DEBUG_TASKS, "Skipping rootTask: (mismatch activity/rootTask) " - + "%s", rootTask); - continue; - } - - mTmpFindTaskResult.process(r, rootTask); - // It is possible to have tasks in multiple root tasks with the same root affinity, so - // we should keep looking after finding an affinity match to see if there is a - // better match in another root task. Also, task affinity isn't a good enough reason - // to target a display which isn't the source of the intent, so skip any affinity - // matches not on the specified display. - if (mTmpFindTaskResult.mRecord != null) { - if (mTmpFindTaskResult.mIdealMatch) { - result.setTo(mTmpFindTaskResult); - return; - } else if (isPreferredDisplayArea) { - // Note: since the traversing through the root tasks is top down, the floating - // tasks should always have lower priority than any affinity-matching tasks - // in the fullscreen root tasks - result.setTo(mTmpFindTaskResult); - } - } - } - } - void onSplitScreenModeDismissed() { // The focused task could be a non-resizeable fullscreen root task that is on top of the // other split-screen tasks, therefore had to dismiss split-screen, make sure the current @@ -1492,7 +1372,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { * @param activityType The activity type under consideration. * @return true if the windowing mode is supported. */ - private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow, + static boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow, boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip, int activityType) { diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 929d7bf03a9e..089071f5de54 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -480,9 +479,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // We want to defer the task appear signal until the task is fully created and attached to // to the hierarchy so that the complete starting configuration is in the task info we send // over to the organizer. - final Task task = display.getDefaultTaskDisplayArea().createRootTask(windowingMode, - ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), - true /* createdByOrganizer */, true /* deferTaskAppear */, launchCookie); + final Task task = new Task.Builder(mService) + .setWindowingMode(windowingMode) + .setIntent(new Intent()) + .setCreatedByOrganizer(true) + .setDeferTaskAppear(true) + .setLaunchCookie(launchCookie) + .setParent(display.getDefaultTaskDisplayArea()) + .build(); task.setDeferTaskAppear(false /* deferTaskAppear */); return task; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 8b8617d8bf3a..21aa6bf8750a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -314,11 +314,12 @@ public class ActivityStackTests extends WindowTestsBase { final RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.process(r, task); + result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info); + result.process(task); assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */)); assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */)); - assertNotNull(result.mRecord); + assertNotNull(result.mIdealRecord); } @Test @@ -340,15 +341,17 @@ public class ActivityStackTests extends WindowTestsBase { final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent( target).setTargetActivity(targetActivity).build(); RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult(); - result.process(r1, parentTask); - assertThat(result.mRecord).isNotNull(); + result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info); + result.process(parentTask); + assertThat(result.mIdealRecord).isNotNull(); // Using alias activity to find task. final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent( alias).setTargetActivity(targetActivity).build(); result = new RootWindowContainer.FindTaskResult(); - result.process(r2, parentTask); - assertThat(result.mRecord).isNotNull(); + result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info); + result.process(parentTask); + assertThat(result.mIdealRecord).isNotNull(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 93ef126b895a..4b42d5603b55 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -324,7 +324,9 @@ public class RecentTasksTest extends WindowTestsBase { .setActivityType(ACTIVITY_TYPE_UNDEFINED) .setFlags(FLAG_ACTIVITY_NEW_TASK) .build(); - assertThat(task1.getActivityType()).isEqualTo(ACTIVITY_TYPE_UNDEFINED); + // Set the activity type again or the activity type of a root task would be remapped + // to ACTIVITY_TYPE_STANDARD, {@link TaskDisplayArea#createRootTask()} + task1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_UNDEFINED); mRecentTasks.add(task1); mCallbacksRecorder.clear(); @@ -347,7 +349,9 @@ public class RecentTasksTest extends WindowTestsBase { .setFlags(FLAG_ACTIVITY_NEW_TASK) .setUserId(TEST_USER_0_ID) .build(); - assertEquals(ACTIVITY_TYPE_UNDEFINED, task1.getActivityType()); + // Set the activity type again or the activity type of a root task would be remapped + // to ACTIVITY_TYPE_STANDARD, {@link TaskDisplayArea#createRootTask()} + task1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_UNDEFINED); mRecentTasks.add(task1); mCallbacksRecorder.clear(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 1c93e0f7e185..10d2da00a852 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -43,8 +43,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; @@ -175,15 +173,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { } @Test - public void testReuseTaskAsStack() { + public void testReuseTaskAsRootTask() { final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); - final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, mDisplayContent); - final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); - doReturn(newStack).when(taskDisplayArea).createRootTask(anyInt(), anyInt(), anyBoolean(), - any(), any(), anyBoolean()); - final int type = ACTIVITY_TYPE_STANDARD; assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask, true /* reuseCandidate */); @@ -359,8 +351,8 @@ public class TaskDisplayAreaTests extends WindowTestsBase { private void assertGetOrCreateRootTask(int windowingMode, int activityType, Task candidateTask, boolean reuseCandidate) { final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); - final Task stack = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType, + final Task rootTask = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType, false /* onTop */, null /* intent */, candidateTask /* candidateTask */); - assertEquals(reuseCandidate, stack == candidateTask); + assertEquals(reuseCandidate, rootTask == candidateTask); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 184995d5b3b9..f20513dd369f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -571,9 +571,11 @@ public class TaskRecordTests extends WindowTestsBase { info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME; info.targetActivity = targetClassName; - final Task task = new Task(mAtm, 1 /* taskId */, info, intent, - null /* voiceSession */, null /* voiceInteractor */, null /* taskDescriptor */, - null /*stack*/); + final Task task = new Task.Builder(mAtm) + .setTaskId(1) + .setActivityInfo(info) + .setIntent(intent) + .build(); assertEquals("The alias activity component should be saved in task intent.", aliasClassName, task.intent.getComponent().getClassName()); @@ -1122,10 +1124,11 @@ public class TaskRecordTests extends WindowTestsBase { } private Task createTask(int taskId) { - return new Task(mAtm, taskId, new Intent(), null, null, null, - ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null, - 0, false, null, 0, 0, 0, 0, null, null, 0, false, false, false, 0, - 0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/, - null /*stack*/); + return new Task.Builder(mAtm) + .setTaskId(taskId) + .setIntent(new Intent()) + .setRealActivity(ActivityBuilder.getDefaultComponent()) + .setEffectiveUid(10050) + .buildInner(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ba5ca2e250e8..31e2dce008de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1055,23 +1055,25 @@ class WindowTestsBase extends SystemServiceTestsBase { mIntent.setFlags(mFlags); } - Task task; - final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId(); + final Task.Builder builder = new Task.Builder(mSupervisor.mService) + .setTaskId(mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId()) + .setWindowingMode(mWindowingMode) + .setActivityInfo(mActivityInfo) + .setIntent(mIntent) + .setOnTop(mOnTop) + .setVoiceSession(mVoiceSession); + final Task task; if (mParentTask == null) { - task = mTaskDisplayArea.createRootTaskUnchecked( - mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, mIntent, - false /* createdByOrganizer */, false /* deferTaskAppear */, - null /* launchCookie */); + task = builder.setActivityType(mActivityType) + .setParent(mTaskDisplayArea) + .build(); } else { - task = new Task(mSupervisor.mService, taskId, mActivityInfo, - mIntent /*intent*/, mVoiceSession, null /*_voiceInteractor*/, - null /*taskDescription*/, mParentTask); + task = builder.setParent(mParentTask).build(); mParentTask.moveToFront("build-task"); - mParentTask.addChild(task, true, true); } spyOn(task); task.mUserId = mUserId; - Task rootTask = task.getRootTask(); + final Task rootTask = task.getRootTask(); if (task != rootTask && !Mockito.mockingDetails(rootTask).isSpy()) { spyOn(rootTask); } diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index 70f6386aa891..8e1875168a84 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -25,7 +25,6 @@ import android.content.ServiceConnection import android.net.ConnectivityManager import android.net.IDnsResolver import android.net.INetd -import android.net.INetworkPolicyManager import android.net.INetworkStatsService import android.net.LinkProperties import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL @@ -88,8 +87,6 @@ class ConnectivityServiceIntegrationTest { @Mock private lateinit var statsService: INetworkStatsService @Mock - private lateinit var policyManager: INetworkPolicyManager - @Mock private lateinit var log: IpConnectivityLog @Mock private lateinit var netd: INetd @@ -171,7 +168,7 @@ class ConnectivityServiceIntegrationTest { } private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService( - context, netManager, statsService, policyManager, dnsResolver, log, netd, deps) + context, netManager, statsService, dnsResolver, log, netd, deps) private fun makeDependencies(): ConnectivityService.Dependencies { val deps = spy(ConnectivityService.Dependencies()) diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 35eb6bafb740..0e8bfe707d7e 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -166,7 +166,6 @@ import android.net.INetd; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; -import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; @@ -183,6 +182,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; +import android.net.NetworkPolicyManager; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; @@ -365,7 +365,6 @@ public class ConnectivityServiceTest { @Mock INetworkManagementService mNetworkManagementService; @Mock INetworkStatsService mStatsService; @Mock IBatteryStats mBatteryStatsService; - @Mock INetworkPolicyManager mNpm; @Mock IDnsResolver mMockDnsResolver; @Mock INetd mMockNetd; @Mock NetworkStackClient mNetworkStack; @@ -380,6 +379,7 @@ public class ConnectivityServiceTest { @Mock TelephonyManager mTelephonyManager; @Mock MockableSystemProperties mSystemProperties; @Mock EthernetManager mEthernetManager; + @Mock NetworkPolicyManager mNetworkPolicyManager; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -477,6 +477,7 @@ public class ConnectivityServiceTest { if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager; if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager; + if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager; return super.getSystemService(name); } @@ -1326,7 +1327,6 @@ public class ConnectivityServiceTest { mService = new ConnectivityService(mServiceContext, mNetworkManagementService, mStatsService, - mNpm, mMockDnsResolver, mock(IpConnectivityLog.class), mMockNetd, @@ -1336,7 +1336,7 @@ public class ConnectivityServiceTest { final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor = ArgumentCaptor.forClass(INetworkPolicyListener.class); - verify(mNpm).registerListener(policyListenerCaptor.capture()); + verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture()); mPolicyListener = policyListenerCaptor.getValue(); // Create local CM before sending system ready so that we can answer |