diff options
48 files changed, 962 insertions, 231 deletions
diff --git a/api/current.txt b/api/current.txt index 8c6207fc5286..85f238c1113d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7206,8 +7206,9 @@ package android.app.slice { field public static final java.lang.String HINT_ACTIONS = "actions"; field public static final java.lang.String HINT_ERROR = "error"; field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; - field public static final java.lang.String HINT_KEY_WORDS = "key_words"; + field public static final java.lang.String HINT_KEYWORDS = "keywords"; field public static final java.lang.String HINT_LARGE = "large"; + field public static final java.lang.String HINT_LAST_UPDATED = "last_updated"; field public static final java.lang.String HINT_LIST = "list"; field public static final java.lang.String HINT_LIST_ITEM = "list_item"; field public static final java.lang.String HINT_NO_TINT = "no_tint"; @@ -7217,10 +7218,12 @@ package android.app.slice { field public static final java.lang.String HINT_SHORTCUT = "shortcut"; field public static final java.lang.String HINT_SUMMARY = "summary"; field public static final java.lang.String HINT_TITLE = "title"; + field public static final java.lang.String HINT_TTL = "ttl"; field public static final java.lang.String SUBTYPE_COLOR = "color"; field public static final java.lang.String SUBTYPE_CONTENT_DESCRIPTION = "content_description"; field public static final java.lang.String SUBTYPE_MAX = "max"; field public static final java.lang.String SUBTYPE_MESSAGE = "message"; + field public static final java.lang.String SUBTYPE_MILLIS = "millis"; field public static final java.lang.String SUBTYPE_PRIORITY = "priority"; field public static final java.lang.String SUBTYPE_RANGE = "range"; field public static final deprecated java.lang.String SUBTYPE_SLIDER = "slider"; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d3c1e99f1523..4326ee3e75e3 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1219,11 +1219,11 @@ public class Notification implements Parcelable public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName"; /** - * This is set on the notification shown by the activity manager about all apps - * running in the background. It indicates that the notification should be shown - * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE} - * notification currently visible to the user. This is a string array of all - * package names of the apps. + * This is set on the notifications shown by system_server about apps running foreground + * services. It indicates that the notification should be shown + * only if any of the given apps do not already have a properly tagged + * {@link #FLAG_FOREGROUND_SERVICE} notification currently visible to the user. + * This is a string array of all package names of the apps. * @hide */ public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps"; diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index 61679cb4d631..95bb1f682701 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -66,8 +66,10 @@ public final class Slice implements Parcelable { HINT_HORIZONTAL, HINT_PARTIAL, HINT_SEE_MORE, - HINT_KEY_WORDS, + HINT_KEYWORDS, HINT_ERROR, + HINT_TTL, + HINT_LAST_UPDATED, }) @Retention(RetentionPolicy.SOURCE) public @interface SliceHint {} @@ -168,12 +170,20 @@ public final class Slice implements Parcelable { * related to the parent slice. * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}. */ - public static final String HINT_KEY_WORDS = "key_words"; + public static final String HINT_KEYWORDS = "keywords"; /** * A hint to indicate that this slice represents an error. */ public static final String HINT_ERROR = "error"; /** + * Hint indicating an item representing a time-to-live for the content. + */ + public static final String HINT_TTL = "ttl"; + /** + * Hint indicating an item representing when the content was created or last updated. + */ + public static final String HINT_LAST_UPDATED = "last_updated"; + /** * Key to retrieve an extra added to an intent when a control is changed. */ public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE"; @@ -243,6 +253,11 @@ public final class Slice implements Parcelable { * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}. */ public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description"; + /** + * Subtype to tag an item as representing a time in milliseconds since midnight, + * January 1, 1970 UTC. + */ + public static final String SUBTYPE_MILLIS = "millis"; private final SliceItem[] mItems; private final @SliceHint String[] mHints; diff --git a/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java index 219868d663d2..dd97f1ef6a02 100644 --- a/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java +++ b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java @@ -19,6 +19,7 @@ package android.privacy.internal.longitudinalreporting; import android.privacy.DifferentialPrivacyEncoder; import android.privacy.internal.rappor.RapporConfig; import android.privacy.internal.rappor.RapporEncoder; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -48,6 +49,9 @@ import com.android.internal.annotations.VisibleForTesting; */ public class LongitudinalReportingEncoder implements DifferentialPrivacyEncoder { + private static final String TAG = "LongitudinalEncoder"; + private static final boolean DEBUG = false; + // Suffix that will be added to Rappor's encoder id. There's a (relatively) small risk some // other Rappor encoder may re-use the same encoder id. private static final String PRR1_ENCODER_ID = "prr1_encoder_id"; @@ -121,11 +125,18 @@ public class LongitudinalReportingEncoder implements DifferentialPrivacyEncoder @Override public byte[] encodeBoolean(boolean original) { + if (DEBUG) { + Log.d(TAG, "encodeBoolean, encoderId:" + mConfig.getEncoderId() + ", original: " + + original); + } if (mFakeValue != null) { // Use the fake value generated in PRR. original = mFakeValue.booleanValue(); + if (DEBUG) Log.d(TAG, "Use fake value: " + original); } - return mIRREncoder.encodeBoolean(original); + byte[] result = mIRREncoder.encodeBoolean(original); + if (DEBUG) Log.d(TAG, "result: " + ((result[0] & 0x1) != 0)); + return result; } @Override diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 51dd92961f54..957c784087af 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1008,6 +1008,9 @@ public class LockPatternView extends View { mDrawingProfilingStarted = false; } } + if (mFadePattern) { + clearPattern(); + } } private void cancelLineAnimations() { diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java index 98e67c21c1a1..3643ca4a02f7 100644 --- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java +++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java @@ -224,7 +224,9 @@ public class GnssMetrics { s.append("GNSS_KPI_END").append("\n"); GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats(); if (stats != null) { - s.append("Power Metrics").append('\n'); + s.append("Power Metrics").append("\n"); + s.append(" Time on battery (min): " + + stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n"); long[] t = stats.getTimeInGpsSignalQualityLevel(); if (t != null && t.length == NUM_GPS_SIGNAL_QUALITY_LEVELS) { s.append(" Amount of time (while on battery) Top 4 Avg CN0 > " + diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 68293d964601..df2115147fb2 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -56,7 +56,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ SystemUI-tags \ SystemUI-proto -LOCAL_JAVA_LIBRARIES := telephony-common +LOCAL_JAVA_LIBRARIES := telephony-common \ + android.car LOCAL_PACKAGE_NAME := SystemUI LOCAL_PRIVATE_PLATFORM_APIS := true diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 3d49e5c37cf8..3488168b651b 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -205,6 +205,9 @@ <!-- Listen app op changes --> <uses-permission android:name="android.permission.WATCH_APPOPS" /> + <!-- to read and change hvac values in a car --> + <uses-permission android:name="android.car.permission.ADJUST_CAR_CLIMATE" /> + <application android:name=".SystemUIApplication" android:persistent="true" diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml index f432d366e926..ad8604935628 100644 --- a/packages/SystemUI/res/layout/car_facet_button.xml +++ b/packages/SystemUI/res/layout/car_facet_button.xml @@ -42,6 +42,7 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:animateLayoutChanges="true" + android:src="@drawable/car_ic_arrow" android:background="@android:color/transparent" android:scaleType="fitCenter"> </com.android.keyguard.AlphaOptimizedImageButton> diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml new file mode 100644 index 000000000000..a65ff1693797 --- /dev/null +++ b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:background="@drawable/system_bar_background"> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:id="@+id/nav_buttons" + android:orientation="vertical" + android:gravity="top" + android:paddingTop="30dp" + android:layout_weight="1" + android:background="@drawable/system_bar_background" + android:animateLayoutChanges="true"> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/home" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end" + android:src="@drawable/car_ic_overview" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvac" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + systemui:broadcast="true" + android:src="@drawable/car_ic_hvac" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + </LinearLayout> +</com.android.systemui.statusbar.car.CarNavigationBarView> diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml new file mode 100644 index 000000000000..b0488ae6382b --- /dev/null +++ b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:background="@drawable/system_bar_background"> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:orientation="horizontal" + android:id="@+id/nav_buttons" + android:gravity="left" + android:paddingLeft="30dp" + android:layout_weight="1" + android:animateLayoutChanges="true"> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/home" + android:layout_height="match_parent" + android:layout_width="wrap_content" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end" + android:src="@drawable/car_ic_overview" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="30dp" + android:paddingRight="30dp" + /> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvac" + android:layout_height="match_parent" + android:layout_width="wrap_content" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + systemui:broadcast="true" + android:src="@drawable/car_ic_hvac" + android:background="?android:attr/selectableItemBackground" + android:paddingLeft="30dp" + android:paddingRight="30dp" + /> + </LinearLayout> +</com.android.systemui.statusbar.car.CarNavigationBarView> + diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml new file mode 100644 index 000000000000..a65ff1693797 --- /dev/null +++ b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:background="@drawable/system_bar_background"> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:id="@+id/nav_buttons" + android:orientation="vertical" + android:gravity="top" + android:paddingTop="30dp" + android:layout_weight="1" + android:background="@drawable/system_bar_background" + android:animateLayoutChanges="true"> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/home" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end" + android:src="@drawable/car_ic_overview" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvac" + android:layout_height="wrap_content" + android:layout_width="match_parent" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + systemui:broadcast="true" + android:src="@drawable/car_ic_hvac" + android:background="?android:attr/selectableItemBackground" + android:paddingTop="30dp" + android:paddingBottom="30dp" + /> + </LinearLayout> +</com.android.systemui.statusbar.car.CarNavigationBarView> diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml index 158907e03541..f2ef30180bc0 100644 --- a/packages/SystemUI/res/layout/car_status_bar_header.xml +++ b/packages/SystemUI/res/layout/car_status_bar_header.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<!-- Extends RelativeLayout --> +<!-- Extends LinearLayout --> <com.android.systemui.qs.car.CarStatusBarHeader xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" @@ -23,22 +23,21 @@ android:paddingStart="8dp" android:paddingEnd="8dp" > - <include - layout="@layout/system_icons" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" /> + <include layout="@layout/system_icons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical|end" + android:layout_weight="1" + /> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_alignParentEnd="true" - android:layout_centerVertical="true" android:singleLine="true" android:paddingStart="@dimen/status_bar_clock_starting_padding" android:paddingEnd="@dimen/status_bar_clock_end_padding" - systemui:showDark="false" /> + android:gravity="center_vertical|end" + /> </com.android.systemui.qs.car.CarStatusBarHeader> diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml new file mode 100644 index 000000000000..e16014bb8945 --- /dev/null +++ b/packages/SystemUI/res/layout/car_top_navigation_bar.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:background="@drawable/system_bar_background"> + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_clock_end_padding" + android:gravity="center_vertical" + /> + +</com.android.systemui.statusbar.car.CarNavigationBarView> + diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml index aa0d4a05f6cc..22f1618f5474 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml @@ -44,7 +44,6 @@ android:focusable="true" android:contentDescription="@string/accessibility_clear_all" android:text="@string/clear_all_notifications_text" - android:textColor="?attr/wallpaperTextColor" - android:textAllCaps="true"/> + android:textColor="?attr/wallpaperTextColor"/> </FrameLayout> </com.android.systemui.statusbar.FooterView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml index 2e7ab7fe8904..f15ca9e972c2 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_row.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml @@ -55,19 +55,6 @@ android:paddingStart="8dp" /> - <!-- TODO: remove --> - <ImageButton - android:id="@+id/helper" - android:layout_width="48dp" - android:layout_height="@*android:dimen/notification_header_height" - android:layout_gravity="top|end" - android:layout_marginEnd="6dp" - android:src="@drawable/ic_dnd" - android:tint="#FF0000" - android:background="@drawable/ripple_drawable" - android:visibility="visible" - /> - <ViewStub android:layout="@layout/notification_children_container" android:id="@+id/child_container_stub" diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml index b1097c39363c..5e4bd79bcc04 100644 --- a/packages/SystemUI/res/values/attrs_car.xml +++ b/packages/SystemUI/res/values/attrs_car.xml @@ -27,6 +27,13 @@ <attr name="categories" format="string"/> <!-- package names that will be added as extras to the fired intents --> <attr name="packages" format="string" /> + <!-- Alpha value to used when in selected state. Defaults 1f --> + <attr name="selectedAlpha" format="float" /> + <!-- Alpha value to used when in un-selected state. Defaults 0.7f --> + <attr name="unselectedAlpha" format="float" /> + <!-- Render a "more" icon. Defaults true --> + <attr name="useMoreIcon" format="boolean" /> + </declare-styleable> @@ -39,4 +46,11 @@ <!-- start the intent as a broad cast instead of an activity if true--> <attr name="broadcast" format="boolean"/> </declare-styleable> + + <!-- Custom attributes to configure hvac values --> + <declare-styleable name="TemperatureView"> + <attr name="hvacAreaId" format="integer"/> + <attr name="hvacPropertyId" format="integer"/> + <attr name="hvacTempFormat" format="string"/> + </declare-styleable> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 2983df6ec937..816c598daf4f 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -50,6 +50,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; /** @@ -249,6 +250,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mConnectionCallbacks.remove(listener); } + public boolean shouldShowSwipeUpUI() { + return getProxy() != null && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0); + } + public IOverviewProxy getProxy() { return mOverviewProxy; } diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java index 245d240017e1..9459ce1ba827 100644 --- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java @@ -21,6 +21,8 @@ import android.util.ArrayMap; import com.android.systemui.Dependency.DependencyProvider; import com.android.systemui.SystemUIFactory; import com.android.systemui.statusbar.NotificationEntryManager; +import com.android.systemui.statusbar.car.CarFacetButtonController; +import com.android.systemui.statusbar.car.hvac.HvacController; /** * Class factory to provide car specific SystemUI components. @@ -32,5 +34,7 @@ public class CarSystemUIFactory extends SystemUIFactory { super.injectDependencies(providers, context); providers.put(NotificationEntryManager.class, () -> new CarNotificationEntryManager(context)); + providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context)); + providers.put(HvacController.class, () -> new HvacController(context)); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java index 6797bb9dbe82..ec183769c763 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java @@ -19,7 +19,7 @@ import android.graphics.Rect; import android.support.annotation.IdRes; import android.util.AttributeSet; import android.view.View; -import android.widget.RelativeLayout; +import android.widget.LinearLayout; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher; * A view that forms the header of the notification panel. This view will ensure that any * status icons that are displayed are tinted accordingly to the current theme. */ -public class CarStatusBarHeader extends RelativeLayout { +public class CarStatusBarHeader extends LinearLayout { public CarStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 05a5a8ea3484..03b263d2ae95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -183,7 +183,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private AboveShelfChangedListener mAboveShelfChangedListener; private HeadsUpManager mHeadsUpManager; private Consumer<Boolean> mHeadsUpAnimatingAwayListener; - private View mHelperButton; private boolean mChildIsExpanding; private boolean mJustClicked; @@ -401,8 +400,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateLimits(); updateIconVisibilities(); updateShelfIconColor(); - - showBlockingHelperButton(mEntry.userSentiment == USER_SENTIMENT_NEGATIVE); updateRippleAllowed(); } @@ -1426,10 +1423,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView requestLayout(); } - public void showBlockingHelperButton(boolean show) { - mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE); - } - public void showAppOpsIcons(ArraySet<Integer> activeOps) { if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) { mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps); @@ -1459,11 +1452,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded); mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout}; - mHelperButton = findViewById(R.id.helper); - mHelperButton.setOnClickListener(view -> { - doLongClickCallback(); - }); - for (NotificationContentView l : mLayouts) { l.setExpandClickListener(mExpandClickListener); l.setContainingNotification(this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 775faee7fbc3..402d9fddc825 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -641,9 +641,14 @@ public class NotificationData { // this is a foreground-service disclosure for a user that does not need to show one return true; } - if (mFsc.isSystemAlertNotification(sbn) && !mFsc.isSystemAlertWarningNeeded( - sbn.getUserId(), sbn.getPackageName())) { - return true; + if (mFsc.isSystemAlertNotification(sbn)) { + final String[] apps = sbn.getNotification().extras.getStringArray( + Notification.EXTRA_FOREGROUND_APPS); + if (apps != null && apps.length >= 1) { + if (!mFsc.isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) { + return true; + } + } } return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index a2f336eb6398..82ad74e26f0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -165,6 +165,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mIsForeground = (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; mIsForBlockingHelper = isForBlockingHelper; + mAppUid = mSbn.getUid(); int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); @@ -173,9 +174,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } else { // Special behavior for the Default channel if no other channels have been defined. mIsSingleDefaultChannel = mNumNotificationChannels == 1 - && mSingleNotificationChannel.getId() - .equals(NotificationChannel.DEFAULT_CHANNEL_ID) - && numTotalChannels <= 1; + && mSingleNotificationChannel.getId().equals( + NotificationChannel.DEFAULT_CHANNEL_ID) + && numTotalChannels == 1; } try { @@ -210,7 +211,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE); if (info != null) { - mAppUid = mSbn.getUid(); mAppName = String.valueOf(mPm.getApplicationLabel(info)); pkgicon = mPm.getApplicationIcon(info); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index b1e08b81b6e0..fd3a9d5e2bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -350,9 +350,6 @@ public class NotificationViewHierarchyManager { } } - row.showBlockingHelperButton(entry.userSentiment == - NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE); - row.showAppOpsIcons(entry.mActiveAppOps); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java index 53101a5bd61f..5f3e2e358306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java @@ -3,6 +3,7 @@ package com.android.systemui.statusbar.car; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -10,6 +11,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.keyguard.AlphaOptimizedImageButton; +import com.android.systemui.Dependency; import com.android.systemui.R; /** @@ -21,9 +23,6 @@ import com.android.systemui.R; * other music apps installed. */ public class CarFacetButton extends LinearLayout { - private static final float SELECTED_ALPHA = 1f; - private static final float UNSELECTED_ALPHA = 0.7f; - private static final String FACET_FILTER_DELIMITER = ";"; /** * Extra information to be sent to a helper to make the decision of what app to launch when @@ -42,6 +41,10 @@ public class CarFacetButton extends LinearLayout { private String[] mFacetCategories; /** App packages that are allowed to be used with this widget */ private String[] mFacetPackages; + private int mIconResourceId; + private boolean mUseMoreIcon = true; + private float mSelectedAlpha = 1f; + private float mUnselectedAlpha = 1f; public CarFacetButton(Context context, AttributeSet attrs) { @@ -53,6 +56,10 @@ public class CarFacetButton extends LinearLayout { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton); setupIntents(typedArray); setupIcons(typedArray); + CarFacetButtonController carFacetButtonController = Dependency.get( + CarFacetButtonController.class); + carFacetButtonController.addFacetButton(this); + } /** @@ -96,21 +103,25 @@ public class CarFacetButton extends LinearLayout { private void setupIcons(TypedArray styledAttributes) { + mSelectedAlpha = styledAttributes.getFloat( + R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha); + mUnselectedAlpha = styledAttributes.getFloat( + R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha); mIcon = findViewById(R.id.car_nav_button_icon); mIcon.setScaleType(ImageView.ScaleType.CENTER); mIcon.setClickable(false); - mIcon.setAlpha(UNSELECTED_ALPHA); - int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0); - if (iconResourceId == 0) { + mIcon.setAlpha(mUnselectedAlpha); + mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0); + if (mIconResourceId == 0) { throw new RuntimeException("specified icon resource was not found and is required"); } - mIcon.setImageResource(iconResourceId); + mIcon.setImageResource(mIconResourceId); mMoreIcon = findViewById(R.id.car_nav_button_more_icon); mMoreIcon.setClickable(false); - mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow)); - mMoreIcon.setAlpha(UNSELECTED_ALPHA); + mMoreIcon.setAlpha(mSelectedAlpha); mMoreIcon.setVisibility(GONE); + mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true); } /** @@ -145,17 +156,27 @@ public class CarFacetButton extends LinearLayout { /** * Updates the visual state to let the user know if it's been selected. * @param selected true if should update the alpha of the icon to selected, false otherwise - * @param showMoreIcon true if the "more icon" should be shown, false otherwise + * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this + * is ignored if the attribute useMoreIcon is set to false */ public void setSelected(boolean selected, boolean showMoreIcon) { mSelected = selected; if (selected) { - mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); - mMoreIcon.setAlpha(SELECTED_ALPHA); - mIcon.setAlpha(SELECTED_ALPHA); + if (mUseMoreIcon) { + mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); + } + mIcon.setAlpha(mSelectedAlpha); } else { mMoreIcon.setVisibility(GONE); - mIcon.setAlpha(UNSELECTED_ALPHA); + mIcon.setAlpha(mUnselectedAlpha); + } + } + + public void setIcon(Drawable d) { + if (d != null) { + mIcon.setImageDrawable(d); + } else { + mIcon.setImageResource(mIconResourceId); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java index e8c9a5e5693a..284113624cc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java @@ -5,8 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.view.View; -import android.view.ViewGroup; import java.util.HashMap; import java.util.List; @@ -29,39 +27,25 @@ public class CarFacetButtonController { } /** - * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons - * such that it can select and unselect them based on running task chages - * @param bar that may contain CarFacetButtons + * Add facet button to this controller. The expected use is for the facet button + * to get a reference to this controller via {@link com.android.systemui.Dependency} + * and self add. + * @param facetButton */ - public void addCarNavigationBar(CarNavigationBarView bar) { - findFacets(bar); - } - - private void findFacets(ViewGroup root) { - final int childCount = root.getChildCount(); - - for (int i = 0; i < childCount; ++i) { - final View v = root.getChildAt(i); - if (v instanceof CarFacetButton) { - CarFacetButton facetButton = (CarFacetButton) v; - String[] categories = facetButton.getCategories(); - for (int j = 0; j < categories.length; j++) { - String category = categories[j]; - mButtonsByCategory.put(category, facetButton); - } + public void addFacetButton(CarFacetButton facetButton) { + String[] categories = facetButton.getCategories(); + for (int j = 0; j < categories.length; j++) { + String category = categories[j]; + mButtonsByCategory.put(category, facetButton); + } - String[] facetPackages = facetButton.getFacetPackages(); - for (int j = 0; j < facetPackages.length; j++) { - String facetPackage = facetPackages[j]; - mButtonsByPackage.put(facetPackage, facetButton); - } - } else if (v instanceof ViewGroup) { - findFacets((ViewGroup) v); - } + String[] facetPackages = facetButton.getFacetPackages(); + for (int j = 0; j < facetPackages.length; j++) { + String facetPackage = facetPackages[j]; + mButtonsByPackage.put(facetPackage, facetButton); } } - /** * This will unselect the currently selected CarFacetButton and determine which one should be * selected next. It does this by reading the properties on the CarFacetButton and seeing if diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index 1d9ef616d98d..e73b1736f33a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java @@ -22,6 +22,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.LinearLayout; +import android.widget.TextView; import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; @@ -36,9 +37,11 @@ class CarNavigationBarView extends LinearLayout { private LinearLayout mNavButtons; private AlphaOptimizedImageButton mNotificationsButton; private CarStatusBar mCarStatusBar; + private Context mContext; public CarNavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); + mContext = context; } @Override @@ -46,7 +49,9 @@ class CarNavigationBarView extends LinearLayout { mNavButtons = findViewById(R.id.nav_buttons); mNotificationsButton = findViewById(R.id.notifications); - mNotificationsButton.setOnClickListener(this::onNotificationsClick); + if (mNotificationsButton != null) { + mNotificationsButton.setOnClickListener(this::onNotificationsClick); + } } void setStatusBar(CarStatusBar carStatusBar) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index c15a01330534..a95d0a4dc7b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -17,13 +17,9 @@ package com.android.systemui.statusbar.car; import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.content.Intent; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.UserHandle; +import android.os.SystemProperties; import android.util.Log; import android.view.Gravity; import android.view.View; @@ -45,8 +41,8 @@ import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.car.hvac.HvacController; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; -import com.android.systemui.statusbar.phone.NavigationBarView; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -60,6 +56,8 @@ import java.util.Map; public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler { private static final String TAG = "CarStatusBar"; + public static final boolean ENABLE_HVAC_CONNECTION + = !SystemProperties.getBoolean("android.car.hvac.demo", true); private TaskStackListenerImpl mTaskStackListener; @@ -93,6 +91,11 @@ public class CarStatusBar extends StatusBar implements createBatteryController(); mCarBatteryController.startListening(); + + if (ENABLE_HVAC_CONNECTION) { + Log.d(TAG, "Connecting to HVAC service"); + Dependency.get(HvacController.class).connectToCarService(); + } } @Override @@ -164,7 +167,7 @@ public class CarStatusBar extends StatusBar implements @Override protected void createNavigationBar() { - mCarFacetButtonController = new CarFacetButtonController(mContext); + mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); if (mNavigationBarView != null) { return; } @@ -225,7 +228,6 @@ public class CarStatusBar extends StatusBar implements lp.windowAnimations = 0; - mCarFacetButtonController.addCarNavigationBar(mNavigationBarView); mWindowManager.addView(mNavigationBarWindow, lp); } @@ -243,7 +245,6 @@ public class CarStatusBar extends StatusBar implements throw new RuntimeException("Unable to build left nav bar due to missing layout"); } mLeftNavigationBarView.setStatusBar(this); - mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView); WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( widthForSides, LayoutParams.MATCH_PARENT, @@ -275,7 +276,6 @@ public class CarStatusBar extends StatusBar implements throw new RuntimeException("Unable to build right nav bar due to missing layout"); } mRightNavigationBarView.setStatusBar(this); - mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView); WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( widthForSides, LayoutParams.MATCH_PARENT, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java new file mode 100644 index 000000000000..23bf88796da3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.car.hvac; + +import android.car.Car; +import android.car.CarNotConnectedException; +import android.car.hardware.CarPropertyValue; +import android.car.hardware.hvac.CarHvacManager; +import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; +import android.content.ComponentName; +import android.content.Context; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * Manages the connection to the Car service and delegates value changes to the registered + * {@link TemperatureView}s + */ +public class HvacController { + + public static final String TAG = "HvacController"; + public final static int BIND_TO_HVAC_RETRY_DELAY = 5000; + + private Context mContext; + private Handler mHandler; + private Car mCar; + private CarHvacManager mHvacManager; + private HashMap<HvacKey, TemperatureView> mTempComponents = new HashMap<>(); + + public HvacController(Context context) { + mContext = context; + } + + /** + * Create connection to the Car service. Note: call backs from the Car service + * ({@link CarHvacManager}) will happen on the same thread this method was called from. + */ + public void connectToCarService() { + mHandler = new Handler(); + mCar = Car.createCar(mContext, mServiceConnection, mHandler); + if (mCar != null) { + // note: this connect call handles the retries + mCar.connect(); + } + } + + /** + * Registers callbacks and initializes components upon connection. + */ + private ServiceConnection mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + try { + service.linkToDeath(mRestart, 0); + mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE); + mHvacManager.registerCallback(mHardwareCallback); + initComponents(); + } catch (Exception e) { + Log.e(TAG, "Failed to correctly connect to HVAC", e); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + destroyHvacManager(); + } + }; + + private void destroyHvacManager() { + if (mHvacManager != null) { + mHvacManager.unregisterCallback(mHardwareCallback); + mHvacManager = null; + } + } + + /** + * If the connection to car service goes away then restart it. + */ + private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.d(TAG, "Death of HVAC triggering a restart"); + if (mCar != null) { + mCar.disconnect(); + } + destroyHvacManager(); + mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY); + } + }; + + /** + * Add component to list and initialize it if the connection is up. + * @param temperatureView + */ + public void addHvacTextView(TemperatureView temperatureView) { + mTempComponents.put( + new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()), + temperatureView); + initComponent(temperatureView); + } + + private void initComponents() { + Iterator<Map.Entry<HvacKey, TemperatureView>> iterator = + mTempComponents.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry<HvacKey, TemperatureView> next = iterator.next(); + initComponent(next.getValue()); + } + } + + + private void initComponent(TemperatureView view) { + int id = view.getPropertyId(); + int zone = view.getAreaId(); + try { + if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) { + view.setTemp(Float.NaN); + return; + } + view.setTemp(mHvacManager.getFloatProperty(id, zone)); + } catch (CarNotConnectedException e) { + view.setTemp(Float.NaN); + Log.e(TAG, "Failed to get value from hvac service", e); + } + } + + /** + * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to + * match. + */ + private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() { + @Override + public void onChangeEvent(final CarPropertyValue val) { + try { + int areaId = val.getAreaId(); + int propertyId = val.getPropertyId(); + TemperatureView temperatureView = mTempComponents.get( + new HvacKey(propertyId, areaId)); + if (temperatureView != null) { + float value = (float) val.getValue(); + temperatureView.setTemp(value); + } // else the data is not of interest + } catch (Exception e) { + // catch all so we don't take down the sysui if a new data type is + // introduced. + Log.e(TAG, "Failed handling hvac change event", e); + } + } + + @Override + public void onErrorEvent(final int propertyId, final int zone) { + Log.d(TAG, "HVAC error event, propertyId: " + propertyId + + " zone: " + zone); + } + }; + + /** + * Key for storing {@link TemperatureView}s in a hash map + */ + private static class HvacKey { + + int mPropertyId; + int mAreaId; + + public HvacKey(int propertyId, int areaId) { + mPropertyId = propertyId; + mAreaId = areaId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + HvacKey hvacKey = (HvacKey) o; + return mPropertyId == hvacKey.mPropertyId && + mAreaId == hvacKey.mAreaId; + } + + @Override + public int hashCode() { + return Objects.hash(mPropertyId, mAreaId); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java new file mode 100644 index 000000000000..4049ec3aa385 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.car.hvac; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.android.systemui.Dependency; +import com.android.systemui.R; + +/** + * Simple text display of HVAC properties, It is designed to show temperature and is configured in + * the XML. + * XML properties: + * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385) + * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) + * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol) + * + * Note: It registers itself with {@link HvacController} + */ +public class TemperatureView extends TextView { + + private final int mAreaId; + private final int mPropertyId; + private final String mTempFormat; + + public TemperatureView(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView); + mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1); + mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1); + String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat); + mTempFormat = (format == null) ? "%.1f\u00B0" : format; + + // register with controller + HvacController hvacController = Dependency.get(HvacController.class); + hvacController.addHvacTextView(this); + } + + /** + * Formats the float for display + * @param temp - The current temp or NaN + */ + public void setTemp(float temp) { + if (Float.isNaN(temp)) { + setText("--"); + return; + } + setText(String.format(mTempFormat, temp)); + } + + /** + * @return propertiyId Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385) + */ + public int getPropertyId() { + return mPropertyId; + } + + /** + * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1) + */ + public int getAreaId() { + return mAreaId; + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index b4cb088621fd..84582b0278f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -79,7 +79,6 @@ import java.io.PrintWriter; import java.util.function.Consumer; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB; -import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; @@ -385,17 +384,13 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } public boolean isQuickStepSwipeUpEnabled() { - return mOverviewProxyService.getProxy() != null - && isOverviewEnabled() - && ((mOverviewProxyService.getInteractionFlags() - & FLAG_DISABLE_SWIPE_UP) == 0); + return mOverviewProxyService.shouldShowSwipeUpUI() && isOverviewEnabled(); } public boolean isQuickScrubEnabled() { return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true) && mOverviewProxyService.getProxy() != null && isOverviewEnabled() - && ((mOverviewProxyService.getInteractionFlags() - & FLAG_DISABLE_QUICK_SCRUB) == 0); + && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0); } private void updateCarModeIcons(Context ctx) { @@ -468,7 +463,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private KeyButtonDrawable chooseNavigationIconDrawable(Context ctx, @DrawableRes int iconLight, @DrawableRes int iconDark, @DrawableRes int quickStepIconLight, @DrawableRes int quickStepIconDark) { - final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled(); + final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI(); return quickStepEnabled ? getDrawable(ctx, quickStepIconLight, quickStepIconDark) : getDrawable(ctx, iconLight, iconDark); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index c326feeb3d52..33c3ee9b7cba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -363,16 +363,16 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, zenDescription = mContext.getString(R.string.interruption_level_priority); } - if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext) - && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { - volumeVisible = true; - volumeIconId = R.drawable.stat_sys_ringer_silent; - volumeDescription = mContext.getString(R.string.accessibility_ringer_silent); - } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS && + if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { volumeVisible = true; volumeIconId = R.drawable.stat_sys_ringer_vibrate; volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate); + } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS && + audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { + volumeVisible = true; + volumeIconId = R.drawable.stat_sys_ringer_silent; + volumeDescription = mContext.getString(R.string.accessibility_ringer_silent); } if (zenVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java index cc7943b85bc1..8e32a0b44c28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java @@ -26,9 +26,12 @@ import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.SystemProperties; import android.view.DisplayListCanvas; import android.view.RenderNodeAnimator; import android.view.View; +import android.view.ViewConfiguration; import android.view.animation.Interpolator; import com.android.systemui.Interpolators; @@ -56,14 +59,17 @@ public class KeyButtonRipple extends Drawable { private float mGlowAlpha = 0f; private float mGlowScale = 1f; private boolean mPressed; + private boolean mVisible; private boolean mDrawingHardwareGlow; private int mMaxWidth; private boolean mLastDark; private boolean mDark; + private boolean mDelayTouchFeedback; private final Interpolator mInterpolator = new LogInterpolator(); private boolean mSupportHardware; private final View mTargetView; + private final Handler mHandler = new Handler(); private final HashSet<Animator> mRunningAnimations = new HashSet<>(); private final ArrayList<Animator> mTmpArray = new ArrayList<>(); @@ -77,6 +83,10 @@ public class KeyButtonRipple extends Drawable { mDark = darkIntensity >= 0.5f; } + public void setDelayTouchFeedback(boolean delay) { + mDelayTouchFeedback = delay; + } + private Paint getRipplePaint() { if (mRipplePaint == null) { mRipplePaint = new Paint(); @@ -211,7 +221,16 @@ public class KeyButtonRipple extends Drawable { } } + /** + * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch + * is enabled. + */ + public void abortDelayedRipple() { + mHandler.removeCallbacksAndMessages(null); + } + private void cancelAnimations() { + mVisible = false; mTmpArray.addAll(mRunningAnimations); int size = mTmpArray.size(); for (int i = 0; i < size; i++) { @@ -220,11 +239,21 @@ public class KeyButtonRipple extends Drawable { } mTmpArray.clear(); mRunningAnimations.clear(); + mHandler.removeCallbacksAndMessages(null); } private void setPressedSoftware(boolean pressed) { if (pressed) { - enterSoftware(); + if (mDelayTouchFeedback) { + if (mRunningAnimations.isEmpty()) { + mHandler.removeCallbacksAndMessages(null); + mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout()); + } else if (mVisible) { + enterSoftware(); + } + } else { + enterSoftware(); + } } else { exitSoftware(); } @@ -232,6 +261,7 @@ public class KeyButtonRipple extends Drawable { private void enterSoftware() { cancelAnimations(); + mVisible = true; mGlowAlpha = getMaxGlowAlpha(); ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale", 0f, GLOW_MAX_SCALE_FACTOR); @@ -240,6 +270,12 @@ public class KeyButtonRipple extends Drawable { scaleAnimator.addListener(mAnimatorListener); scaleAnimator.start(); mRunningAnimations.add(scaleAnimator); + + // With the delay, it could eventually animate the enter animation with no pressed state, + // then immediately show the exit animation. If this is skipped there will be no ripple. + if (mDelayTouchFeedback && !mPressed) { + exitSoftware(); + } } private void exitSoftware() { @@ -253,7 +289,16 @@ public class KeyButtonRipple extends Drawable { private void setPressedHardware(boolean pressed) { if (pressed) { - enterHardware(); + if (mDelayTouchFeedback) { + if (mRunningAnimations.isEmpty()) { + mHandler.removeCallbacksAndMessages(null); + mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout()); + } else if (mVisible) { + enterHardware(); + } + } else { + enterHardware(); + } } else { exitHardware(); } @@ -302,6 +347,7 @@ public class KeyButtonRipple extends Drawable { private void enterHardware() { cancelAnimations(); + mVisible = true; mDrawingHardwareGlow = true; setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2)); final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(), @@ -343,6 +389,12 @@ public class KeyButtonRipple extends Drawable { mRunningAnimations.add(endAnim); invalidateSelf(); + + // With the delay, it could eventually animate the enter animation with no pressed state, + // then immediately show the exit animation. If this is skipped there will be no ripple. + if (mDelayTouchFeedback && !mPressed) { + exitHardware(); + } } private void exitHardware() { @@ -366,6 +418,7 @@ public class KeyButtonRipple extends Drawable { public void onAnimationEnd(Animator animation) { mRunningAnimations.remove(animation); if (mRunningAnimations.isEmpty() && !mPressed) { + mVisible = false; mDrawingHardwareGlow = false; invalidateSelf(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index e5fefd34ffb7..5d7e9383930a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -65,7 +65,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private int mTouchSlop; private int mTouchDownX; private int mTouchDownY; - private boolean mIsPressed; private boolean mSupportsLongpress = true; private AudioManager mAudioManager; private boolean mGestureAborted; @@ -78,7 +77,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private final Runnable mCheckLongPress = new Runnable() { public void run() { - if (mIsPressed) { + if (isPressed()) { // Log.d("KeyButtonView", "longpressed: " + this); if (isLongClickable()) { // Just an old-fashioned ImageView @@ -89,12 +88,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); mLongClicked = true; } - - // Only when quick step is enabled, ripple will not be shown on touch down, then - // show the ripple on touch up or on long press - if (mLongClicked && mOverviewProxyService.getProxy() != null) { - setPressed(true); - } } } }; @@ -214,9 +207,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { mGestureAborted = false; } if (mGestureAborted) { - if (mIsPressed) { - setPressed(false); - } + setPressed(false); return false; } @@ -224,6 +215,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { case MotionEvent.ACTION_DOWN: mDownTime = SystemClock.uptimeMillis(); mLongClicked = false; + setPressed(true); // Use raw X and Y to detect gestures in case a parent changes the x and y values mTouchDownX = (int) ev.getRawX(); @@ -234,10 +226,8 @@ public class KeyButtonView extends ImageView implements ButtonInterface { // Provide the same haptic feedback that the system offers for virtual keys. performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } - mIsPressed = true; if (!isProxyConnected) { playSoundEffect(SoundEffectConstants.CLICK); - setPressed(mIsPressed); } removeCallbacks(mCheckLongPress); postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout()); @@ -250,10 +240,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { if (exceededTouchSlopX || exceededTouchSlopY) { // When quick step is enabled, prevent animating the ripple triggered by // setPressed and decide to run it on touch up - mIsPressed = false; - if (!isProxyConnected) { - setPressed(mIsPressed); - } + setPressed(false); removeCallbacks(mCheckLongPress); } break; @@ -265,12 +252,11 @@ public class KeyButtonView extends ImageView implements ButtonInterface { removeCallbacks(mCheckLongPress); break; case MotionEvent.ACTION_UP: - final boolean doIt = mIsPressed && !mLongClicked; + final boolean doIt = isPressed() && !mLongClicked; + setPressed(false); final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150; if (isProxyConnected) { if (doIt) { - // Animate the ripple in on touch up with setPressed and then out later - setPressed(true); if (doHapticFeedback) { mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); } @@ -281,7 +267,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { // and it feels weird to sometimes get a release haptic and other times not. performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE); } - setPressed(false); if (mCode != 0) { if (doIt) { // If there was a pending remote recents animation, then we need to @@ -311,12 +296,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface { mAudioManager.playSoundEffect(soundConstant, ActivityManager.getCurrentUser()); } - @Override - public void setPressed(boolean pressed) { - mIsPressed = pressed; - super.setPressed(pressed); - } - public void sendEvent(int action, int flags) { sendEvent(action, flags, SystemClock.uptimeMillis()); } @@ -339,6 +318,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @Override public void abortCurrentGesture() { setPressed(false); + mRipple.abortDelayedRipple(); mGestureAborted = true; } @@ -357,6 +337,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { @Override public void setDelayTouchFeedback(boolean shouldDelay) { + mRipple.setDelayTouchFeedback(shouldDelay); } @Override diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index 107ce1eecf2f..a6b09ced2d81 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -75,7 +75,7 @@ LOCAL_JAVA_LIBRARIES := \ android.test.runner \ telephony-common \ android.test.base \ - + android.car LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java index 2000bff7e99a..c43702119e91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java @@ -226,12 +226,21 @@ public class NotificationDataTest extends SysuiTestCase { public void testSuppressSystemAlertNotification() { when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); when(mFsc.isSystemAlertNotification(any())).thenReturn(true); + StatusBarNotification sbn = mRow.getEntry().notification; + Bundle bundle = new Bundle(); + bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"}); + sbn.getNotification().extras = bundle; assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry().notification)); } @Test public void testDoNotSuppressSystemAlertNotification() { + StatusBarNotification sbn = mRow.getEntry().notification; + Bundle bundle = new Bundle(); + bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"}); + sbn.getNotification().extras = bundle; + when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); when(mFsc.isSystemAlertNotification(any())).thenReturn(true); @@ -249,6 +258,22 @@ public class NotificationDataTest extends SysuiTestCase { } @Test + public void testDoNotSuppressMalformedSystemAlertNotification() { + when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); + + // missing extra + assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification)); + + StatusBarNotification sbn = mRow.getEntry().notification; + Bundle bundle = new Bundle(); + bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {}); + sbn.getNotification().extras = bundle; + + // extra missing values + assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification)); + } + + @Test public void testShouldFilterHiddenNotifications() { // setup when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index 7dda8b268b3e..c2cb5b9940eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -218,6 +218,18 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_DefaultChannelUsesChannelNameIfMoreChannelsExist() + throws Exception { + // Package has one channel by default. + when(mMockINotificationManager.getNumNotificationChannelsForPackage( + eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(VISIBLE, textView.getVisibility()); + } + + @Test public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java index 6907c58f13b0..29b1339e8022 100644 --- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java +++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java @@ -171,7 +171,7 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { Slog.w(TAG, "Only shell is allowed to call network watchlist shell commands"); return; } - (new NetworkWatchlistShellCommand(mContext)).exec(this, in, out, err, args, callback, + (new NetworkWatchlistShellCommand(this, mContext)).exec(this, in, out, err, args, callback, resultReceiver); } @@ -262,6 +262,21 @@ public class NetworkWatchlistService extends INetworkWatchlistManager.Stub { mNetworkWatchlistHandler.reportWatchlistIfNecessary(); } + /** + * Force generate watchlist report for testing. + * + * @param lastReportTime Watchlist report will cotain all records before this time. + * @return True if operation success. + */ + public boolean forceReportWatchlistForTest(long lastReportTime) { + if (mConfig.isConfigSecure()) { + // Should not force generate report under production config. + return false; + } + mNetworkWatchlistHandler.forceReportWatchlistForTest(lastReportTime); + return true; + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java index 9533823df808..17c5868a53f7 100644 --- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java +++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java @@ -19,9 +19,11 @@ package com.android.server.net.watchlist; import android.content.Context; import android.content.Intent; import android.net.NetworkWatchlistManager; +import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ShellCommand; +import android.provider.Settings; import java.io.FileInputStream; import java.io.IOException; @@ -34,10 +36,12 @@ import java.io.PrintWriter; */ class NetworkWatchlistShellCommand extends ShellCommand { - final NetworkWatchlistManager mNetworkWatchlistManager; + final Context mContext; + final NetworkWatchlistService mService; - NetworkWatchlistShellCommand(Context context) { - mNetworkWatchlistManager = new NetworkWatchlistManager(context); + NetworkWatchlistShellCommand(NetworkWatchlistService service, Context context) { + mContext = context; + mService = service; } @Override @@ -51,11 +55,13 @@ class NetworkWatchlistShellCommand extends ShellCommand { switch(cmd) { case "set-test-config": return runSetTestConfig(); + case "force-generate-report": + return runForceGenerateReport(); default: return handleDefaultCommands(cmd); } - } catch (RemoteException e) { - pw.println("Remote exception: " + e); + } catch (Exception e) { + pw.println("Exception: " + e); } return -1; } @@ -73,22 +79,44 @@ class NetworkWatchlistShellCommand extends ShellCommand { WatchlistConfig.getInstance().setTestMode(fileStream); } pw.println("Success!"); - } catch (RuntimeException | IOException ex) { + } catch (Exception ex) { pw.println("Error: " + ex.toString()); return -1; } return 0; } + private int runForceGenerateReport() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final long ident = Binder.clearCallingIdentity(); + try { + // Reset last report time + if (!WatchlistConfig.getInstance().isConfigSecure()) { + pw.println("Error: Cannot force generate report under production config"); + return -1; + } + Settings.Global.putLong(mContext.getContentResolver(), + Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L); + mService.forceReportWatchlistForTest(System.currentTimeMillis()); + pw.println("Success!"); + } catch (Exception ex) { + pw.println("Error: " + ex); + return -1; + } finally { + Binder.restoreCallingIdentity(ident); + } + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); pw.println("Network watchlist manager commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(""); pw.println(" set-test-config your_watchlist_config.xml"); - pw.println(); - Intent.printIntentArgsHelp(pw , ""); + pw.println(" Set network watchlist test config file."); + pw.println(" force-generate-report"); + pw.println(" Force generate watchlist test report."); } } diff --git a/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java b/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java index c1231fa342e7..408a9ed31d18 100644 --- a/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java +++ b/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java @@ -19,6 +19,7 @@ package com.android.server.net.watchlist; import android.privacy.DifferentialPrivacyEncoder; import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig; import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -32,6 +33,7 @@ import java.util.Map; class PrivacyUtils { private static final String TAG = "PrivacyUtils"; + private static final boolean DEBUG = NetworkWatchlistService.DEBUG; /** * Parameters used for encoding watchlist reports. @@ -84,6 +86,7 @@ class PrivacyUtils { @VisibleForTesting static Map<String, Boolean> createDpEncodedReportMap(boolean isSecure, byte[] userSecret, List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult) { + if (DEBUG) Slog.i(TAG, "createDpEncodedReportMap start"); final int appDigestListSize = appDigestList.size(); final HashMap<String, Boolean> resultMap = new HashMap<>(appDigestListSize); for (int i = 0; i < appDigestListSize; i++) { @@ -93,6 +96,7 @@ class PrivacyUtils { ? createSecureDPEncoder(userSecret, appDigest) : createInsecureDPEncoderForTest(appDigest); final boolean visitedWatchlist = aggregatedResult.appDigestList.contains(appDigest); + if (DEBUG) Slog.i(TAG, appDigest + ": " + visitedWatchlist); // Get the least significant bit of first byte, and set result to True if it is 1 boolean encodedVisitedWatchlist = ((int) encoder.encodeBoolean(visitedWatchlist)[0] & 0x1) == 0x1; diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java index e8b39c04a830..b331b9c0d9ae 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java @@ -43,6 +43,7 @@ import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.GregorianCalendar; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -61,6 +62,8 @@ class WatchlistLoggingHandler extends Handler { static final int LOG_WATCHLIST_EVENT_MSG = 1; @VisibleForTesting static final int REPORT_RECORDS_IF_NECESSARY_MSG = 2; + @VisibleForTesting + static final int FORCE_REPORT_RECORDS_NOW_FOR_TEST_MSG = 3; private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1); private static final String DROPBOX_TAG = "network_watchlist_report"; @@ -110,7 +113,15 @@ class WatchlistLoggingHandler extends Handler { break; } case REPORT_RECORDS_IF_NECESSARY_MSG: - tryAggregateRecords(); + tryAggregateRecords(getLastMidnightTime()); + break; + case FORCE_REPORT_RECORDS_NOW_FOR_TEST_MSG: + if (msg.obj instanceof Long) { + long lastRecordTime = (Long) msg.obj; + tryAggregateRecords(lastRecordTime); + } else { + Slog.e(TAG, "Msg.obj needs to be a Long object."); + } break; default: { Slog.d(TAG, "WatchlistLoggingHandler received an unknown of message."); @@ -146,6 +157,12 @@ class WatchlistLoggingHandler extends Handler { sendMessage(msg); } + public void forceReportWatchlistForTest(long lastReportTime) { + final Message msg = obtainMessage(FORCE_REPORT_RECORDS_NOW_FOR_TEST_MSG); + msg.obj = lastReportTime; + sendMessage(msg); + } + /** * Insert network traffic event to watchlist async queue processor. */ @@ -177,8 +194,14 @@ class WatchlistLoggingHandler extends Handler { } private boolean insertRecord(int uid, String cncHost, long timestamp) { + if (DEBUG) { + Slog.i(TAG, "trying to insert record with host: " + cncHost + ", uid: " + uid); + } if (!mConfig.isConfigSecure() && !isPackageTestOnly(uid)) { // Skip package if config is not secure and package is not TestOnly app. + if (DEBUG) { + Slog.i(TAG, "uid: " + uid + " is not test only package"); + } return true; } final byte[] digest = getDigestFromUid(uid); @@ -187,50 +210,56 @@ class WatchlistLoggingHandler extends Handler { return false; } final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp); - tryAggregateRecords(); return result; } - private boolean shouldReportNetworkWatchlist() { + private boolean shouldReportNetworkWatchlist(long lastRecordTime) { final long lastReportTime = Settings.Global.getLong(mResolver, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L); - final long currentTimestamp = System.currentTimeMillis(); - if (currentTimestamp < lastReportTime) { + if (lastRecordTime < lastReportTime) { Slog.i(TAG, "Last report time is larger than current time, reset report"); - mDbHelper.cleanup(); + mDbHelper.cleanup(lastReportTime); return false; } - return currentTimestamp >= lastReportTime + ONE_DAY_MS; + return lastRecordTime >= lastReportTime + ONE_DAY_MS; } - private void tryAggregateRecords() { - // Check if it's necessary to generate watchlist report now. - if (!shouldReportNetworkWatchlist()) { - Slog.i(TAG, "No need to aggregate record yet."); - return; - } - Slog.i(TAG, "Start aggregating watchlist records."); - if (mDropBoxManager != null && mDropBoxManager.isTagEnabled(DROPBOX_TAG)) { - Settings.Global.putLong(mResolver, - Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, - System.currentTimeMillis()); - final WatchlistReportDbHelper.AggregatedResult aggregatedResult = - mDbHelper.getAggregatedRecords(); - if (aggregatedResult == null) { - Slog.i(TAG, "Cannot get result from database"); + private void tryAggregateRecords(long lastRecordTime) { + long startTime = System.currentTimeMillis(); + try { + // Check if it's necessary to generate watchlist report now. + if (!shouldReportNetworkWatchlist(lastRecordTime)) { + Slog.i(TAG, "No need to aggregate record yet."); return; } - // Get all digests for watchlist report, it should include all installed - // application digests and previously recorded app digests. - final List<String> digestsForReport = getAllDigestsForReport(aggregatedResult); - final byte[] secretKey = mSettings.getPrivacySecretKey(); - final byte[] encodedResult = ReportEncoder.encodeWatchlistReport(mConfig, - secretKey, digestsForReport, aggregatedResult); - if (encodedResult != null) { - addEncodedReportToDropBox(encodedResult); + Slog.i(TAG, "Start aggregating watchlist records."); + if (mDropBoxManager != null && mDropBoxManager.isTagEnabled(DROPBOX_TAG)) { + Settings.Global.putLong(mResolver, + Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, + lastRecordTime); + final WatchlistReportDbHelper.AggregatedResult aggregatedResult = + mDbHelper.getAggregatedRecords(lastRecordTime); + if (aggregatedResult == null) { + Slog.i(TAG, "Cannot get result from database"); + return; + } + // Get all digests for watchlist report, it should include all installed + // application digests and previously recorded app digests. + final List<String> digestsForReport = getAllDigestsForReport(aggregatedResult); + final byte[] secretKey = mSettings.getPrivacySecretKey(); + final byte[] encodedResult = ReportEncoder.encodeWatchlistReport(mConfig, + secretKey, digestsForReport, aggregatedResult); + if (encodedResult != null) { + addEncodedReportToDropBox(encodedResult); + } + } else { + Slog.w(TAG, "Network Watchlist dropbox tag is not enabled"); } + mDbHelper.cleanup(lastRecordTime); + } finally { + long endTime = System.currentTimeMillis(); + Slog.i(TAG, "Milliseconds spent on tryAggregateRecords(): " + (endTime - startTime)); } - mDbHelper.cleanup(); } /** @@ -379,4 +408,19 @@ class WatchlistLoggingHandler extends Handler { } return subDomainList.toArray(new String[0]); } + + static long getLastMidnightTime() { + return getMidnightTimestamp(0); + } + + static long getMidnightTimestamp(int daysBefore) { + java.util.Calendar date = new GregorianCalendar(); + // reset hour, minutes, seconds and millis + date.set(java.util.Calendar.HOUR_OF_DAY, 0); + date.set(java.util.Calendar.MINUTE, 0); + date.set(java.util.Calendar.SECOND, 0); + date.set(java.util.Calendar.MILLISECOND, 0); + date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore); + return date.getTimeInMillis(); + } } diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java index 4b577bb9c919..632ab81b131e 100644 --- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java +++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java @@ -141,11 +141,10 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper { } /** - * Aggregate all records before most recent local midnight in database, and return a + * Aggregate all records in database before input timestamp, and return a * rappor encoded result. */ - public AggregatedResult getAggregatedRecords() { - final long lastMidnightTime = getLastMidnightTime(); + public AggregatedResult getAggregatedRecords(long untilTimestamp) { final String selectStatement = WhiteListReportContract.TIMESTAMP + " < ?"; final SQLiteDatabase db = getReadableDatabase(); @@ -153,7 +152,7 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper { try { c = db.query(true /* distinct */, WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement, - new String[]{"" + lastMidnightTime}, null, null, + new String[]{Long.toString(untilTimestamp)}, null, null, null, null); if (c == null) { return null; @@ -181,29 +180,13 @@ class WatchlistReportDbHelper extends SQLiteOpenHelper { } /** - * Remove all the records before most recent local midnight. + * Remove all the records before input timestamp. * * @return True if success. */ - public boolean cleanup() { + public boolean cleanup(long untilTimestamp) { final SQLiteDatabase db = getWritableDatabase(); - final long midnightTime = getLastMidnightTime(); - final String clause = WhiteListReportContract.TIMESTAMP + "< " + midnightTime; + final String clause = WhiteListReportContract.TIMESTAMP + "< " + untilTimestamp; return db.delete(WhiteListReportContract.TABLE, clause, null) != 0; } - - static long getLastMidnightTime() { - return getMidnightTimestamp(0); - } - - static long getMidnightTimestamp(int daysBefore) { - java.util.Calendar date = new GregorianCalendar(); - // reset hour, minutes, seconds and millis - date.set(java.util.Calendar.HOUR_OF_DAY, 0); - date.set(java.util.Calendar.MINUTE, 0); - date.set(java.util.Calendar.SECOND, 0); - date.set(java.util.Calendar.MILLISECOND, 0); - date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore); - return date.getTimeInMillis(); - } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 775fdaa8d7ca..e08ec556c68f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -808,7 +808,7 @@ public class PackageManagerService extends IPackageManager.Stub } final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages, - String targetPath) { + String targetPath, Object installLock) { if (overlayPackages == null || overlayPackages.isEmpty()) { return null; } @@ -828,7 +828,16 @@ public class PackageManagerService extends IPackageManager.Stub // // OverlayManagerService will update each of them with a correct gid from its // target package app id. - synchronized (mInstallLock) { + if (installLock != null) { + synchronized (installLock) { + mInstaller.idmap(targetPath, overlayPackage.baseCodePath, + UserHandle.getSharedAppGid( + UserHandle.getUserGid(UserHandle.USER_SYSTEM))); + } + } else { + // We can call mInstaller without holding mInstallLock because mInstallLock + // is held before running parallel parsing. + // Moreover holding mInstallLock on each parsing thread causes dead-lock. mInstaller.idmap(targetPath, overlayPackage.baseCodePath, UserHandle.getSharedAppGid( UserHandle.getUserGid(UserHandle.USER_SYSTEM))); @@ -853,7 +862,7 @@ public class PackageManagerService extends IPackageManager.Stub } // It is safe to keep overlayPackages without holding mPackages because static overlay // packages can't be uninstalled or disabled. - return getStaticOverlayPaths(overlayPackages, targetPath); + return getStaticOverlayPaths(overlayPackages, targetPath, mInstallLock); } @Override public final String[] getOverlayApks(String targetPackageName) { @@ -890,7 +899,7 @@ public class PackageManagerService extends IPackageManager.Stub return mOverlayPackages == null ? null : getStaticOverlayPaths( getStaticOverlayPackages(mOverlayPackages, targetPackageName), - targetPath); + targetPath, null); } } diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java index 9b787deb8298..9177d253e464 100644 --- a/services/core/java/com/android/server/wm/AlertWindowNotification.java +++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java @@ -37,6 +37,8 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; + import com.android.internal.R; import com.android.server.policy.IconUtilities; @@ -109,6 +111,8 @@ class AlertWindowNotification { final String message = context.getString(R.string.alert_windows_notification_message, appName); + Bundle extras = new Bundle(); + extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {mPackageName}); final Notification.Builder builder = new Notification.Builder(context, mNotificationTag) .setOngoing(true) .setContentTitle( @@ -118,6 +122,7 @@ class AlertWindowNotification { .setColor(context.getColor(R.color.system_notification_accent_color)) .setStyle(new Notification.BigTextStyle().bigText(message)) .setLocalOnly(true) + .addExtras(extras) .setContentIntent(getContentIntent(context, mPackageName)); if (aInfo != null) { diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java index 9dc07c1659db..da04a0d1075c 100644 --- a/telephony/java/android/telephony/MbmsDownloadSession.java +++ b/telephony/java/android/telephony/MbmsDownloadSession.java @@ -920,11 +920,11 @@ public class MbmsDownloadSession implements AutoCloseable { try { if (!token.createNewFile()) { throw new RuntimeException("Failed to create download token for request " - + request); + + request + ". Token location is " + token.getPath()); } } catch (IOException e) { throw new RuntimeException("Failed to create download token for request " + request - + " due to IOException " + e); + + " due to IOException " + e + ". Attempted to write to " + token.getPath()); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index da5bd84f9083..15e06321206a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -7752,11 +7752,25 @@ public class TelephonyManager { */ public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED = 0x4; + /** + * The indication for link capacity estimate update. + * @hide + */ + public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE = 0x8; + + /** + * The indication for physical channel config update. + * @hide + */ + public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG = 0x10; + /** @hide */ @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = { INDICATION_FILTER_SIGNAL_STRENGTH, INDICATION_FILTER_FULL_NETWORK_STATE, - INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED + INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED, + INDICATION_FILTER_LINK_CAPACITY_ESTIMATE, + INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG }) @Retention(RetentionPolicy.SOURCE) public @interface IndicationFilters{} diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java index b0c00c6284a6..fe7533f57b12 100644 --- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java +++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java @@ -297,7 +297,9 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { for (Uri tempFileUri : tempFiles) { if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) { File tempFile = new File(tempFileUri.getSchemeSpecificPart()); - tempFile.delete(); + if (!tempFile.delete()) { + Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath()); + } } } } @@ -474,6 +476,8 @@ public class MbmsDownloadReceiver extends BroadcastReceiver { if (!MbmsUtils.isContainedIn( MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) { + Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," + + " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId)); return false; } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index ee7084ad86c0..d25fd3fdf60c 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -419,6 +419,8 @@ cat include/telephony/ril.h | \ int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145; int RIL_REQUEST_START_KEEPALIVE = 146; int RIL_REQUEST_STOP_KEEPALIVE = 147; + int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148; + int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149; int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; |