diff options
Diffstat (limited to 'packages/ExtServices')
22 files changed, 0 insertions, 2508 deletions
diff --git a/packages/ExtServices/Android.bp b/packages/ExtServices/Android.bp deleted file mode 100644 index db94eec17ce8..000000000000 --- a/packages/ExtServices/Android.bp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2016 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -android_app { - name: "ExtServices", - srcs: ["src/**/*.java"], - platform_apis: true, - certificate: "platform", - aaptflags: ["--shared-lib"], - export_package_resources: true, - optimize: { - proguard_flags_files: ["proguard.proguard"], - }, - privileged: true, -} diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml deleted file mode 100644 index 45e557c00333..000000000000 --- a/packages/ExtServices/AndroidManifest.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - package="android.ext.services" - android:versionCode="1" - android:versionName="1" - coreApp="true"> - - <uses-permission android:name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" /> - - <application android:label="@string/app_name" - android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true"> - - <service android:name=".storage.CacheQuotaServiceImpl" - android:permission="android.permission.BIND_CACHE_QUOTA_SERVICE"> - <intent-filter> - <action android:name="android.app.usage.CacheQuotaService" /> - </intent-filter> - </service> - - <service android:name=".resolver.LRResolverRankerService" - android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE" - android:priority="-1" > - <intent-filter> - <action android:name="android.service.resolver.ResolverRankerService" /> - </intent-filter> - </service> - - <service android:name=".notification.Assistant" - android:label="@string/notification_assistant" - android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE" - android:exported="true"> - <intent-filter> - <action android:name="android.service.notification.NotificationAssistantService" /> - </intent-filter> - </service> - - <service android:name=".autofill.AutofillFieldClassificationServiceImpl" - android:permission="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"> - <intent-filter> - <action android:name="android.service.autofill.AutofillFieldClassificationService" /> - </intent-filter> - <meta-data - android:name="android.autofill.field_classification.default_algorithm" - android:resource="@string/autofill_field_classification_default_algorithm" /> - <meta-data - android:name="android.autofill.field_classification.available_algorithms" - android:resource="@array/autofill_field_classification_available_algorithms" /> - </service> - - <library android:name="android.ext.services"/> - </application> - -</manifest> diff --git a/packages/ExtServices/MODULE_LICENSE_APACHE2 b/packages/ExtServices/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/packages/ExtServices/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/packages/ExtServices/NOTICE b/packages/ExtServices/NOTICE deleted file mode 100644 index c5b1efa7aac7..000000000000 --- a/packages/ExtServices/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/packages/ExtServices/OWNERS b/packages/ExtServices/OWNERS deleted file mode 100644 index 7640b919ca53..000000000000 --- a/packages/ExtServices/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -baligh@google.com -delphij@google.com diff --git a/packages/ExtServices/proguard.proguard b/packages/ExtServices/proguard.proguard deleted file mode 100644 index e5dfbe1c453d..000000000000 --- a/packages/ExtServices/proguard.proguard +++ /dev/null @@ -1,7 +0,0 @@ --keepparameternames --keepattributes Exceptions,InnerClasses,Signature,Deprecated, - SourceFile,LineNumberTable,*Annotation*,EnclosingMethod - --keep public class * { - public protected *; -} diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml deleted file mode 100644 index 72647ab8ae3f..000000000000 --- a/packages/ExtServices/res/values/strings.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name">Android Services Library</string> - - <string name="notification_assistant">Notification Assistant</string> - <string name="prompt_block_reason">Too many dismissals:views</string> - - <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string> - <string-array name="autofill_field_classification_available_algorithms"> - <item>EDIT_DISTANCE</item> - </string-array> -</resources> diff --git a/packages/ExtServices/src/android/ext/services/Version.java b/packages/ExtServices/src/android/ext/services/Version.java deleted file mode 100644 index 026cccd37334..000000000000 --- a/packages/ExtServices/src/android/ext/services/Version.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services; - -/** - * Class that provides the version of the library. - */ -public final class Version { - - private Version() { - /* do nothing - hide constructor */ - } - - /** - * Gets the version of the library. - * - * @return The version. - */ - public static int getVersionCode() { - return 1; - } -}
\ No newline at end of file diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java deleted file mode 100644 index 9ba7e092f34b..000000000000 --- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.ext.services.autofill; - -import static android.ext.services.autofill.EditDistanceScorer.DEFAULT_ALGORITHM; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; -import android.service.autofill.AutofillFieldClassificationService; -import android.util.Log; -import android.view.autofill.AutofillValue; - -import com.android.internal.util.ArrayUtils; - -import java.util.List; - -public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService { - - private static final String TAG = "AutofillFieldClassificationServiceImpl"; - - @Nullable - @Override - public float[][] onGetScores(@Nullable String algorithmName, - @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues, - @NonNull List<String> userDataValues) { - if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) { - Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues (" - + userDataValues + ")"); - return null; - } - if (algorithmName != null && !algorithmName.equals(DEFAULT_ALGORITHM)) { - Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using " - + DEFAULT_ALGORITHM + " instead"); - } - - return EditDistanceScorer.getScores(actualValues, userDataValues); - } -} diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java deleted file mode 100644 index 302b16022c26..000000000000 --- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.ext.services.autofill; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.Log; -import android.view.autofill.AutofillValue; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.List; - -final class EditDistanceScorer { - - private static final String TAG = "EditDistanceScorer"; - - // TODO(b/70291841): STOPSHIP - set to false before launching - private static final boolean DEBUG = true; - - static final String DEFAULT_ALGORITHM = "EDIT_DISTANCE"; - - /** - * Gets the field classification score of 2 values based on the edit distance between them. - * - * <p>The score is defined as: @(max_length - edit_distance) / max_length - */ - @VisibleForTesting - static float getScore(@Nullable AutofillValue actualValue, @Nullable String userDataValue) { - if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0; - - final String actualValueText = actualValue.getTextValue().toString(); - final int actualValueLength = actualValueText.length(); - final int userDatalength = userDataValue.length(); - if (userDatalength == 0) { - return (actualValueLength == 0) ? 1 : 0; - } - - final int distance = editDistance(actualValueText.toLowerCase(), - userDataValue.toLowerCase()); - final int maxLength = Math.max(actualValueLength, userDatalength); - return ((float) maxLength - distance) / maxLength; - } - - /** - * Computes the edit distance (number of insertions, deletions or substitutions to edit one - * string into the other) between two strings. In particular, this will compute the Levenshtein - * distance. - * - * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details. - * - * @param s the first string to compare - * @param t the second string to compare - * @return the edit distance between the two strings - */ - // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java - public static int editDistance(@NonNull String s, @NonNull String t) { - return editDistance(s, t, Integer.MAX_VALUE); - } - - /** - * Computes the edit distance (number of insertions, deletions or substitutions to edit one - * string into the other) between two strings. In particular, this will compute the Levenshtein - * distance. - * - * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details. - * - * @param s the first string to compare - * @param t the second string to compare - * @param max the maximum edit distance that we care about; if for example the string length - * delta is greater than this we don't bother computing the exact edit distance since the - * caller has indicated they're not interested in the result - * @return the edit distance between the two strings, or some other value greater than that if - * the edit distance is at least as big as the {@code max} parameter - */ - // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java - private static int editDistance(@NonNull String s, @NonNull String t, int max) { - if (s.equals(t)) { - return 0; - } - - if (Math.abs(s.length() - t.length()) > max) { - // The string lengths differ more than the allowed edit distance; - // no point in even attempting to compute the edit distance (requires - // O(n*m) storage and O(n*m) speed, where n and m are the string lengths) - return Integer.MAX_VALUE; - } - - int m = s.length(); - int n = t.length(); - int[][] d = new int[m + 1][n + 1]; - for (int i = 0; i <= m; i++) { - d[i][0] = i; - } - for (int j = 0; j <= n; j++) { - d[0][j] = j; - } - for (int j = 1; j <= n; j++) { - for (int i = 1; i <= m; i++) { - if (s.charAt(i - 1) == t.charAt(j - 1)) { - d[i][j] = d[i - 1][j - 1]; - } else { - int deletion = d[i - 1][j] + 1; - int insertion = d[i][j - 1] + 1; - int substitution = d[i - 1][j - 1] + 1; - d[i][j] = Math.min(deletion, Math.min(insertion, substitution)); - } - } - } - - return d[m][n]; - } - /** - * Gets the scores in a batch. - */ - static float[][] getScores(@NonNull List<AutofillValue> actualValues, - @NonNull List<String> userDataValues) { - final int actualValuesSize = actualValues.size(); - final int userDataValuesSize = userDataValues.size(); - if (DEBUG) { - Log.d(TAG, "getScores() will return a " + actualValuesSize + "x" - + userDataValuesSize + " matrix for " + DEFAULT_ALGORITHM); - } - final float[][] scores = new float[actualValuesSize][userDataValuesSize]; - - for (int i = 0; i < actualValuesSize; i++) { - for (int j = 0; j < userDataValuesSize; j++) { - final float score = getScore(actualValues.get(i), userDataValues.get(j)); - scores[i][j] = score; - } - } - return scores; - } - -} diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java deleted file mode 100644 index f8788226fc51..000000000000 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ /dev/null @@ -1,382 +0,0 @@ -/** - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.notification; - -import static android.app.NotificationManager.IMPORTANCE_MIN; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; - -import android.app.INotificationManager; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.ext.services.R; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.storage.StorageManager; -import android.provider.Settings; -import android.service.notification.Adjustment; -import android.service.notification.NotificationAssistantService; -import android.service.notification.NotificationStats; -import android.service.notification.StatusBarNotification; -import android.util.ArrayMap; -import android.util.AtomicFile; -import android.util.Log; -import android.util.Slog; -import android.util.Xml; - -import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.XmlUtils; - -import libcore.io.IoUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Map; - -/** - * Notification assistant that provides guidance on notification channel blocking - */ -public class Assistant extends NotificationAssistantService { - private static final String TAG = "ExtAssistant"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private static final String TAG_ASSISTANT = "assistant"; - private static final String TAG_IMPRESSION = "impression-set"; - private static final String ATT_KEY = "key"; - private static final int DB_VERSION = 1; - private static final String ATTR_VERSION = "version"; - - private static final ArrayList<Integer> PREJUDICAL_DISMISSALS = new ArrayList<>(); - static { - PREJUDICAL_DISMISSALS.add(REASON_CANCEL); - PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL); - } - - private float mDismissToViewRatioLimit; - private int mStreakLimit; - - // key : impressions tracker - // TODO: prune deleted channels and apps - final ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>(); - // SBN key : channel id - ArrayMap<String, String> mLiveNotifications = new ArrayMap<>(); - - private Ranking mFakeRanking = null; - private AtomicFile mFile = null; - - public Assistant() { - } - - @Override - public void onCreate() { - super.onCreate(); - // Contexts are correctly hooked up by the creation step, which is required for the observer - // to be hooked up/initialized. - new SettingsObserver(mHandler); - } - - private void loadFile() { - if (DEBUG) Slog.d(TAG, "loadFile"); - AsyncTask.execute(() -> { - InputStream infile = null; - try { - infile = mFile.openRead(); - readXml(infile); - } catch (FileNotFoundException e) { - Log.d(TAG, "File doesn't exist or isn't readable yet"); - } catch (IOException e) { - Log.e(TAG, "Unable to read channel impressions", e); - } catch (NumberFormatException | XmlPullParserException e) { - Log.e(TAG, "Unable to parse channel impressions", e); - } finally { - IoUtils.closeQuietly(infile); - } - }); - } - - protected void readXml(InputStream stream) - throws XmlPullParserException, NumberFormatException, IOException { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); - final int outerDepth = parser.getDepth(); - while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (!TAG_ASSISTANT.equals(parser.getName())) { - continue; - } - final int impressionOuterDepth = parser.getDepth(); - while (XmlUtils.nextElementWithin(parser, impressionOuterDepth)) { - if (!TAG_IMPRESSION.equals(parser.getName())) { - continue; - } - String key = parser.getAttributeValue(null, ATT_KEY); - ChannelImpressions ci = createChannelImpressionsWithThresholds(); - ci.populateFromXml(parser); - synchronized (mkeyToImpressions) { - ci.append(mkeyToImpressions.get(key)); - mkeyToImpressions.put(key, ci); - } - } - } - } - - private void saveFile() throws IOException { - AsyncTask.execute(() -> { - final FileOutputStream stream; - try { - stream = mFile.startWrite(); - } catch (IOException e) { - Slog.w(TAG, "Failed to save policy file", e); - return; - } - try { - final XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); - writeXml(out); - mFile.finishWrite(stream); - } catch (IOException e) { - Slog.w(TAG, "Failed to save impressions file, restoring backup", e); - mFile.failWrite(stream); - } - }); - } - - protected void writeXml(XmlSerializer out) throws IOException { - out.startDocument(null, true); - out.startTag(null, TAG_ASSISTANT); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); - synchronized (mkeyToImpressions) { - for (Map.Entry<String, ChannelImpressions> entry - : mkeyToImpressions.entrySet()) { - // TODO: ensure channel still exists - out.startTag(null, TAG_IMPRESSION); - out.attribute(null, ATT_KEY, entry.getKey()); - entry.getValue().writeXml(out); - out.endTag(null, TAG_IMPRESSION); - } - } - out.endTag(null, TAG_ASSISTANT); - out.endDocument(); - } - - @Override - public Adjustment onNotificationEnqueued(StatusBarNotification sbn) { - if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey()); - return null; - } - - @Override - public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { - if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); - try { - Ranking ranking = getRanking(sbn.getKey(), rankingMap); - if (ranking != null && ranking.getChannel() != null) { - String key = getKey( - sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); - ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, - createChannelImpressionsWithThresholds()); - if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) { - adjustNotification(createNegativeAdjustment( - sbn.getPackageName(), sbn.getKey(), sbn.getUserId())); - } - mkeyToImpressions.put(key, ci); - mLiveNotifications.put(sbn.getKey(), ranking.getChannel().getId()); - } - } catch (Throwable e) { - Log.e(TAG, "Error occurred processing post", e); - } - } - - @Override - public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, - NotificationStats stats, int reason) { - try { - boolean updatedImpressions = false; - String channelId = mLiveNotifications.remove(sbn.getKey()); - String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId); - synchronized (mkeyToImpressions) { - ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, - createChannelImpressionsWithThresholds()); - if (stats.hasSeen()) { - ci.incrementViews(); - updatedImpressions = true; - } - if (PREJUDICAL_DISMISSALS.contains(reason)) { - if ((!sbn.isAppGroup() || sbn.getNotification().isGroupChild()) - && !stats.hasInteracted() - && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD - && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK - && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) { - if (DEBUG) Log.i(TAG, "increment dismissals " + key); - ci.incrementDismissals(); - updatedImpressions = true; - } else { - if (DEBUG) Slog.i(TAG, "reset streak " + key); - if (ci.getStreak() > 0) { - updatedImpressions = true; - } - ci.resetStreak(); - } - } - mkeyToImpressions.put(key, ci); - } - if (updatedImpressions) { - saveFile(); - } - } catch (Throwable e) { - Slog.e(TAG, "Error occurred processing removal", e); - } - } - - @Override - public void onNotificationSnoozedUntilContext(StatusBarNotification sbn, - String snoozeCriterionId) { - } - - @Override - public void onListenerConnected() { - if (DEBUG) Log.i(TAG, "CONNECTED"); - try { - mFile = new AtomicFile(new File(new File( - Environment.getDataUserCePackageDirectory( - StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()), - "assistant"), "blocking_helper_stats.xml")); - loadFile(); - for (StatusBarNotification sbn : getActiveNotifications()) { - onNotificationPosted(sbn); - } - } catch (Throwable e) { - Log.e(TAG, "Error occurred on connection", e); - } - } - - protected String getKey(String pkg, int userId, String channelId) { - return pkg + "|" + userId + "|" + channelId; - } - - private Ranking getRanking(String key, RankingMap rankingMap) { - if (mFakeRanking != null) { - return mFakeRanking; - } - Ranking ranking = new Ranking(); - rankingMap.getRanking(key, ranking); - return ranking; - } - - private Adjustment createNegativeAdjustment(String packageName, String key, int user) { - if (DEBUG) Log.d(TAG, "User probably doesn't want " + key); - Bundle signals = new Bundle(); - signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE); - return new Adjustment(packageName, key, signals, - getContext().getString(R.string.prompt_block_reason), user); - } - - // for testing - - protected void setFile(AtomicFile file) { - mFile = file; - } - - protected void setFakeRanking(Ranking ranking) { - mFakeRanking = ranking; - } - - protected void setNoMan(INotificationManager noMan) { - mNoMan = noMan; - } - - protected void setContext(Context context) { - mSystemContext = context; - } - - protected ChannelImpressions getImpressions(String key) { - synchronized (mkeyToImpressions) { - return mkeyToImpressions.get(key); - } - } - - protected void insertImpressions(String key, ChannelImpressions ci) { - synchronized (mkeyToImpressions) { - mkeyToImpressions.put(key, ci); - } - } - - private ChannelImpressions createChannelImpressionsWithThresholds() { - ChannelImpressions impressions = new ChannelImpressions(); - impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); - return impressions; - } - - /** - * Observer for updates on blocking helper threshold values. - */ - private final class SettingsObserver extends ContentObserver { - private final Uri STREAK_LIMIT_URI = - Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT); - private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI = - Settings.Global.getUriFor( - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT); - - public SettingsObserver(Handler handler) { - super(handler); - ContentResolver resolver = getApplicationContext().getContentResolver(); - resolver.registerContentObserver( - DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId()); - resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId()); - - // Update all uris on creation. - update(null); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - update(uri); - } - - private void update(Uri uri) { - ContentResolver resolver = getApplicationContext().getContentResolver(); - if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) { - mDismissToViewRatioLimit = Settings.Global.getFloat( - resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, - ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT); - } - if (uri == null || STREAK_LIMIT_URI.equals(uri)) { - mStreakLimit = Settings.Global.getInt( - resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, - ChannelImpressions.DEFAULT_STREAK_LIMIT); - } - - // Update all existing channel impression objects with any new limits/thresholds. - synchronized (mkeyToImpressions) { - for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) { - channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); - } - } - } - } -}
\ No newline at end of file diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java deleted file mode 100644 index 29ee920d4dde..000000000000 --- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.notification; - -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; - -public final class ChannelImpressions implements Parcelable { - private static final String TAG = "ExtAssistant.CI"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .8f; - static final int DEFAULT_STREAK_LIMIT = 2; - static final String ATT_DISMISSALS = "dismisses"; - static final String ATT_VIEWS = "views"; - static final String ATT_STREAK = "streak"; - - private int mDismissals = 0; - private int mViews = 0; - private int mStreak = 0; - - private float mDismissToViewRatioLimit; - private int mStreakLimit; - - public ChannelImpressions() { - mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT; - mStreakLimit = DEFAULT_STREAK_LIMIT; - } - - protected ChannelImpressions(Parcel in) { - mDismissals = in.readInt(); - mViews = in.readInt(); - mStreak = in.readInt(); - mDismissToViewRatioLimit = in.readFloat(); - mStreakLimit = in.readInt(); - } - - public int getStreak() { - return mStreak; - } - - public int getDismissals() { - return mDismissals; - } - - public int getViews() { - return mViews; - } - - public void incrementDismissals() { - mDismissals++; - mStreak++; - } - - void updateThresholds(float dismissToViewRatioLimit, int streakLimit) { - mDismissToViewRatioLimit = dismissToViewRatioLimit; - mStreakLimit = streakLimit; - } - - @VisibleForTesting - float getDismissToViewRatioLimit() { - return mDismissToViewRatioLimit; - } - - @VisibleForTesting - int getStreakLimit() { - return mStreakLimit; - } - - public void append(ChannelImpressions additionalImpressions) { - if (additionalImpressions != null) { - mViews += additionalImpressions.getViews(); - mStreak += additionalImpressions.getStreak(); - mDismissals += additionalImpressions.getDismissals(); - } - } - - public void incrementViews() { - mViews++; - } - - public void resetStreak() { - mStreak = 0; - } - - public boolean shouldTriggerBlock() { - if (getViews() == 0) { - return false; - } - if (DEBUG) { - Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak()); - } - return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit - && getStreak() > mStreakLimit; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mDismissals); - dest.writeInt(mViews); - dest.writeInt(mStreak); - dest.writeFloat(mDismissToViewRatioLimit); - dest.writeInt(mStreakLimit); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator<ChannelImpressions> CREATOR = new Creator<ChannelImpressions>() { - @Override - public ChannelImpressions createFromParcel(Parcel in) { - return new ChannelImpressions(in); - } - - @Override - public ChannelImpressions[] newArray(int size) { - return new ChannelImpressions[size]; - } - }; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ChannelImpressions that = (ChannelImpressions) o; - - if (mDismissals != that.mDismissals) return false; - if (mViews != that.mViews) return false; - return mStreak == that.mStreak; - } - - @Override - public int hashCode() { - int result = mDismissals; - result = 31 * result + mViews; - result = 31 * result + mStreak; - return result; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("ChannelImpressions{"); - sb.append("mDismissals=").append(mDismissals); - sb.append(", mViews=").append(mViews); - sb.append(", mStreak=").append(mStreak); - sb.append(", thresholds=(").append(mDismissToViewRatioLimit); - sb.append(",").append(mStreakLimit); - sb.append(")}"); - return sb.toString(); - } - - protected void populateFromXml(XmlPullParser parser) { - mDismissals = safeInt(parser, ATT_DISMISSALS, 0); - mStreak = safeInt(parser, ATT_STREAK, 0); - mViews = safeInt(parser, ATT_VIEWS, 0); - } - - protected void writeXml(XmlSerializer out) throws IOException { - if (mDismissals != 0) { - out.attribute(null, ATT_DISMISSALS, String.valueOf(mDismissals)); - } - if (mStreak != 0) { - out.attribute(null, ATT_STREAK, String.valueOf(mStreak)); - } - if (mViews != 0) { - out.attribute(null, ATT_VIEWS, String.valueOf(mViews)); - } - } - - private static int safeInt(XmlPullParser parser, String att, int defValue) { - final String val = parser.getAttributeValue(null, att); - return tryParseInt(val, defValue); - } - - private static int tryParseInt(String value, int defValue) { - if (TextUtils.isEmpty(value)) return defValue; - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return defValue; - } - } -} diff --git a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java b/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java deleted file mode 100644 index 9d7a5689dcd1..000000000000 --- a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.resolver; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Environment; -import android.os.IBinder; -import android.os.storage.StorageManager; -import android.service.resolver.ResolverRankerService; -import android.service.resolver.ResolverTarget; -import android.util.ArrayMap; -import android.util.Log; - -import java.io.File; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used - * in {@link ResolverComparator}. - */ -public final class LRResolverRankerService extends ResolverRankerService { - private static final String TAG = "LRResolverRankerService"; - - private static final boolean DEBUG = false; - - private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; - private static final String BIAS_PREF_KEY = "bias"; - private static final String VERSION_PREF_KEY = "version"; - - private static final String LAUNCH_SCORE = "launch"; - private static final String TIME_SPENT_SCORE = "timeSpent"; - private static final String RECENCY_SCORE = "recency"; - private static final String CHOOSER_SCORE = "chooser"; - - // parameters for a pre-trained model, to initialize the app ranker. When updating the - // pre-trained model, please update these params, as well as initModel(). - private static final int CURRENT_VERSION = 1; - private static final float LEARNING_RATE = 0.0001f; - private static final float REGULARIZER_PARAM = 0.0001f; - - private SharedPreferences mParamSharedPref; - private ArrayMap<String, Float> mFeatureWeights; - private float mBias; - - @Override - public IBinder onBind(Intent intent) { - initModel(); - return super.onBind(intent); - } - - @Override - public void onPredictSharingProbabilities(List<ResolverTarget> targets) { - final int size = targets.size(); - for (int i = 0; i < size; ++i) { - ResolverTarget target = targets.get(i); - ArrayMap<String, Float> features = getFeatures(target); - target.setSelectProbability(predict(features)); - } - } - - @Override - public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) { - final int size = targets.size(); - if (selectedPosition < 0 || selectedPosition >= size) { - if (DEBUG) { - Log.d(TAG, "Invalid Position of Selected App " + selectedPosition); - } - return; - } - final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition)); - final float positiveProbability = targets.get(selectedPosition).getSelectProbability(); - final int targetSize = targets.size(); - for (int i = 0; i < targetSize; ++i) { - if (i == selectedPosition) { - continue; - } - final ArrayMap<String, Float> negative = getFeatures(targets.get(i)); - final float negativeProbability = targets.get(i).getSelectProbability(); - if (negativeProbability > positiveProbability) { - update(negative, negativeProbability, false); - update(positive, positiveProbability, true); - } - } - commitUpdate(); - } - - private void initModel() { - mParamSharedPref = getParamSharedPref(); - mFeatureWeights = new ArrayMap<>(4); - if (mParamSharedPref == null || - mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) { - // Initializing the app ranker to a pre-trained model. When updating the pre-trained - // model, please increment CURRENT_VERSION, and update LEARNING_RATE and - // REGULARIZER_PARAM. - mBias = -1.6568f; - mFeatureWeights.put(LAUNCH_SCORE, 2.5543f); - mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f); - mFeatureWeights.put(RECENCY_SCORE, 0.269f); - mFeatureWeights.put(CHOOSER_SCORE, 4.2222f); - } else { - mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f); - mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f)); - mFeatureWeights.put( - TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f)); - mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f)); - mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f)); - } - } - - private ArrayMap<String, Float> getFeatures(ResolverTarget target) { - ArrayMap<String, Float> features = new ArrayMap<>(4); - features.put(RECENCY_SCORE, target.getRecencyScore()); - features.put(TIME_SPENT_SCORE, target.getTimeSpentScore()); - features.put(LAUNCH_SCORE, target.getLaunchScore()); - features.put(CHOOSER_SCORE, target.getChooserScore()); - return features; - } - - private float predict(ArrayMap<String, Float> target) { - if (target == null) { - return 0.0f; - } - final int featureSize = target.size(); - float sum = 0.0f; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float weight = mFeatureWeights.getOrDefault(featureName, 0.0f); - sum += weight * target.valueAt(i); - } - return (float) (1.0 / (1.0 + Math.exp(-mBias - sum))); - } - - private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) { - if (target == null) { - return; - } - final int featureSize = target.size(); - float error = isSelected ? 1.0f - predict : -predict; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f); - mBias += LEARNING_RATE * error; - currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight + - LEARNING_RATE * error * target.valueAt(i); - mFeatureWeights.put(featureName, currentWeight); - } - if (DEBUG) { - Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias); - } - } - - private void commitUpdate() { - try { - SharedPreferences.Editor editor = mParamSharedPref.edit(); - editor.putFloat(BIAS_PREF_KEY, mBias); - final int size = mFeatureWeights.size(); - for (int i = 0; i < size; i++) { - editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i)); - } - editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION); - editor.apply(); - } catch (Exception e) { - Log.e(TAG, "Failed to commit update" + e); - } - } - - private SharedPreferences getParamSharedPref() { - // The package info in the context isn't initialized in the way it is for normal apps, - // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we - // build the path manually below using the same policy that appears in ContextImpl. - if (DEBUG) { - Log.d(TAG, "Context Package Name: " + getPackageName()); - } - final File prefsFile = new File(new File( - Environment.getDataUserCePackageDirectory( - StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()), - "shared_prefs"), - PARAM_SHARED_PREF_NAME + ".xml"); - return getSharedPreferences(prefsFile, Context.MODE_PRIVATE); - } -}
\ No newline at end of file diff --git a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java b/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java deleted file mode 100644 index 862f50b2b627..000000000000 --- a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.storage; - -import android.app.usage.CacheQuotaHint; -import android.app.usage.CacheQuotaService; -import android.os.Environment; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.util.ArrayMap; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * CacheQuotaServiceImpl implements the CacheQuotaService with a strategy for populating the quota - * of {@link CacheQuotaHint}. - */ -public class CacheQuotaServiceImpl extends CacheQuotaService { - private static final double CACHE_RESERVE_RATIO = 0.15; - - @Override - public List<CacheQuotaHint> onComputeCacheQuotaHints(List<CacheQuotaHint> requests) { - ArrayMap<String, List<CacheQuotaHint>> byUuid = new ArrayMap<>(); - final int requestCount = requests.size(); - for (int i = 0; i < requestCount; i++) { - CacheQuotaHint request = requests.get(i); - String uuid = request.getVolumeUuid(); - List<CacheQuotaHint> listForUuid = byUuid.get(uuid); - if (listForUuid == null) { - listForUuid = new ArrayList<>(); - byUuid.put(uuid, listForUuid); - } - listForUuid.add(request); - } - - List<CacheQuotaHint> processed = new ArrayList<>(); - byUuid.entrySet().forEach( - requestListEntry -> { - // Collapse all usage stats to the same uid. - Map<Integer, List<CacheQuotaHint>> byUid = requestListEntry.getValue() - .stream() - .collect(Collectors.groupingBy(CacheQuotaHint::getUid)); - byUid.values().forEach(uidGroupedList -> { - int size = uidGroupedList.size(); - if (size < 2) { - return; - } - CacheQuotaHint first = uidGroupedList.get(0); - for (int i = 1; i < size; i++) { - /* Note: We can't use the UsageStats built-in addition function because - UIDs may span multiple packages and usage stats adding has - matching package names as a precondition. */ - first.getUsageStats().mTotalTimeInForeground += - uidGroupedList.get(i).getUsageStats().mTotalTimeInForeground; - } - }); - - // Because the foreground stats have been added to the first element, we need - // a list of only the first values (which contain the merged foreground time). - List<CacheQuotaHint> flattenedRequests = - byUid.values() - .stream() - .map(entryList -> entryList.get(0)) - .filter(entry -> entry.getUsageStats().mTotalTimeInForeground != 0) - .sorted(sCacheQuotaRequestComparator) - .collect(Collectors.toList()); - - // Because the elements are sorted, we can use the index to also be the sorted - // index for cache quota calculation. - double sum = getSumOfFairShares(flattenedRequests.size()); - String uuid = requestListEntry.getKey(); - long reservedSize = getReservedCacheSize(uuid); - for (int count = 0; count < flattenedRequests.size(); count++) { - double share = getFairShareForPosition(count) / sum; - CacheQuotaHint entry = flattenedRequests.get(count); - CacheQuotaHint.Builder builder = new CacheQuotaHint.Builder(entry); - builder.setQuota(Math.round(share * reservedSize)); - processed.add(builder.build()); - } - } - ); - - return processed.stream() - .filter(request -> request.getQuota() > 0).collect(Collectors.toList()); - } - - private double getFairShareForPosition(int position) { - double value = 1.0 / Math.log(position + 3) - 0.285; - return (value > 0.01) ? value : 0.01; - } - - private double getSumOfFairShares(int size) { - double sum = 0; - for (int i = 0; i < size; i++) { - sum += getFairShareForPosition(i); - } - return sum; - } - - private long getReservedCacheSize(String uuid) { - // TODO: Revisit the cache size after running more storage tests. - // TODO: Figure out how to ensure ExtServices has the permissions to call - // StorageStatsManager, because this is ignoring the cache... - StorageManager storageManager = getSystemService(StorageManager.class); - long freeBytes = 0; - if (uuid == StorageManager.UUID_PRIVATE_INTERNAL) { // regular equals because of null - freeBytes = Environment.getDataDirectory().getUsableSpace(); - } else { - final VolumeInfo vol = storageManager.findVolumeByUuid(uuid); - freeBytes = vol.getPath().getUsableSpace(); - } - return Math.round(freeBytes * CACHE_RESERVE_RATIO); - } - - // Compares based upon foreground time. - private static Comparator<CacheQuotaHint> sCacheQuotaRequestComparator = - new Comparator<CacheQuotaHint>() { - @Override - public int compare(CacheQuotaHint o, CacheQuotaHint t1) { - long x = t1.getUsageStats().getTotalTimeInForeground(); - long y = o.getUsageStats().getTotalTimeInForeground(); - return (x < y) ? -1 : ((x == y) ? 0 : 1); - } - }; -} diff --git a/packages/ExtServices/tests/Android.bp b/packages/ExtServices/tests/Android.bp deleted file mode 100644 index db160277b82e..000000000000 --- a/packages/ExtServices/tests/Android.bp +++ /dev/null @@ -1,19 +0,0 @@ -android_test { - name: "ExtServicesUnitTests", - certificate: "platform", - libs: [ - "android.test.runner", - "android.test.base", - ], - static_libs: [ - "androidx.test.rules", - "mockito-target-minus-junit4", - "androidx.test.espresso.core", - "truth-prebuilt", - "testables", - ], - // Include all test java files. - srcs: ["src/**/*.java"], - platform_apis: true, - instrumentation_for: "ExtServices", -} diff --git a/packages/ExtServices/tests/AndroidManifest.xml b/packages/ExtServices/tests/AndroidManifest.xml deleted file mode 100644 index 42293b5a0b3e..000000000000 --- a/packages/ExtServices/tests/AndroidManifest.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.ext.services.tests.unit"> - - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.ext.services" - android:label="ExtServices Test Cases"> - </instrumentation> - -</manifest>
\ No newline at end of file diff --git a/packages/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml deleted file mode 100644 index cd26ebc407c1..000000000000 --- a/packages/ExtServices/tests/AndroidTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Runs Tests for ExtServices"> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> - <option name="test-file-name" value="ExtServicesUnitTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="framework-base-presubmit" /> - <option name="test-tag" value="ExtServicesUnitTests" /> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="android.ext.services.tests.unit" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <option name="hidden-api-checks" value="false"/> - </test> -</configuration>
\ No newline at end of file diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java deleted file mode 100644 index 48c076e67e78..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.autofill; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; - -import static com.google.common.truth.Truth.assertThat; - -import android.view.autofill.AutofillValue; - -/** - * Contains the base tests that does not rely on the specific algorithm implementation. - */ -public class AutofillFieldClassificationServiceImplTest { - - private final AutofillFieldClassificationServiceImpl mService = - new AutofillFieldClassificationServiceImpl(); - - @Test - public void testOnGetScores_nullActualValues() { - assertThat(mService.onGetScores(null, null, null, Arrays.asList("whatever"))).isNull(); - } - - @Test - public void testOnGetScores_emptyActualValues() { - assertThat(mService.onGetScores(null, null, Collections.emptyList(), - Arrays.asList("whatever"))).isNull(); - } - - @Test - public void testOnGetScores_nullUserDataValues() { - assertThat(mService.onGetScores(null, null, - Arrays.asList(AutofillValue.forText("whatever")), null)).isNull(); - } - - @Test - public void testOnGetScores_emptyUserDataValues() { - assertThat(mService.onGetScores(null, null, - Arrays.asList(AutofillValue.forText("whatever")), Collections.emptyList())) - .isNull(); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java deleted file mode 100644 index afe223641d37..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.ext.services.autofill; - -import static android.ext.services.autofill.EditDistanceScorer.getScore; -import static android.ext.services.autofill.EditDistanceScorer.getScores; -import static android.view.autofill.AutofillValue.forText; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import android.view.autofill.AutofillValue; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class EditDistanceScorerTest { - - @Test - public void testGetScore_nullValue() { - assertFloat(getScore(null, "D'OH!"), 0); - } - - @Test - public void testGetScore_nonTextValue() { - assertFloat(getScore(AutofillValue.forToggle(true), "D'OH!"), 0); - } - - @Test - public void testGetScore_nullUserData() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), null), 0); - } - - @Test - public void testGetScore_fullMatch() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1); - assertFloat(getScore(AutofillValue.forText(""), ""), 1); - } - - @Test - public void testGetScore_fullMatchMixedCase() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1); - } - - @Test - public void testGetScore_mismatchDifferentSizes() { - assertFloat(getScore(AutofillValue.forText("X"), "Xy"), 0.50F); - assertFloat(getScore(AutofillValue.forText("Xy"), "X"), 0.50F); - assertFloat(getScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F); - assertFloat(getScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F); - assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Parkway"), - "1600 Amphitheatre Pkwy"), 0.88F); - assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Pkwy"), - "1600 Amphitheatre Parkway"), 0.88F); - } - - @Test - public void testGetScore_partialMatch() { - assertFloat(getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F); - assertFloat(getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F); - assertFloat(getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F); - assertFloat(getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F); - assertFloat(getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F); - assertFloat(getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F); - } - - @Test - public void testGetScores() { - final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b")); - final List<String> userDataValues = Arrays.asList("a", "B", "ab", "c"); - final float[][] expectedScores = new float[][] { - new float[] { 1F, 0F, 0.5F, 0F }, - new float[] { 0F, 1F, 0.5F, 0F } - }; - final float[][] actualScores = getScores(actualValues, userDataValues); - - // Unfortunately, Truth does not have an easy way to compare float matrices and show useful - // messages in case of error, so we need to check. - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores.length).isEqualTo(2); - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores[0].length).isEqualTo(4); - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores[1].length).isEqualTo(4); - for (int i = 0; i < actualScores.length; i++) { - final float[] line = actualScores[i]; - for (int j = 0; j < line.length; j++) { - float cell = line[j]; - assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F) - .of(expectedScores[i][j]); - } - } - } - - public static void assertFloat(float actualValue, float expectedValue) { - assertThat(actualValue).isWithin(0.01F).of(expectedValue); - } - - public static String toString(float[][] matrix) { - final StringBuilder string = new StringBuilder("[ "); - for (int i = 0; i < matrix.length; i++) { - string.append(Arrays.toString(matrix[i])).append(" "); - } - return string.append(" ]").toString(); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java deleted file mode 100644 index 6ef25e553204..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java +++ /dev/null @@ -1,447 +0,0 @@ -/** - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.notification; - -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static android.app.NotificationManager.IMPORTANCE_LOW; -import static android.app.NotificationManager.IMPORTANCE_MIN; - -import static junit.framework.Assert.assertEquals; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Application; -import android.app.INotificationManager; -import android.app.Notification; -import android.app.NotificationChannel; -import android.content.ContentResolver; -import android.content.Intent; -import android.os.UserHandle; -import android.provider.Settings; -import android.service.notification.Adjustment; -import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.Ranking; -import android.service.notification.NotificationListenerService.RankingMap; -import android.service.notification.NotificationStats; -import android.service.notification.StatusBarNotification; -import android.test.ServiceTestCase; -import android.testing.TestableContext; -import android.util.AtomicFile; - -import androidx.test.InstrumentationRegistry; - -import com.android.internal.util.FastXmlSerializer; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.xmlpull.v1.XmlSerializer; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; - -public class AssistantTest extends ServiceTestCase<Assistant> { - - private static final String PKG1 = "pkg1"; - private static final int UID1 = 1; - private static final NotificationChannel P1C1 = - new NotificationChannel("one", "", IMPORTANCE_LOW); - private static final NotificationChannel P1C2 = - new NotificationChannel("p1c2", "", IMPORTANCE_DEFAULT); - private static final NotificationChannel P1C3 = - new NotificationChannel("p1c3", "", IMPORTANCE_MIN); - private static final String PKG2 = "pkg2"; - - private static final int UID2 = 2; - private static final NotificationChannel P2C1 = - new NotificationChannel("one", "", IMPORTANCE_LOW); - - @Mock INotificationManager mNoMan; - @Mock AtomicFile mFile; - - Assistant mAssistant; - Application mApplication; - - @Rule - public final TestableContext mContext = - new TestableContext(InstrumentationRegistry.getContext(), null); - - public AssistantTest() { - super(Assistant.class); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Intent startIntent = - new Intent("android.service.notification.NotificationAssistantService"); - startIntent.setPackage("android.ext.services"); - - // To bypass real calls to global settings values, set the Settings values here. - Settings.Global.putFloat(mContext.getContentResolver(), - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2); - mApplication = (Application) InstrumentationRegistry.getInstrumentation(). - getTargetContext().getApplicationContext(); - // Force the test to use the correct application instead of trying to use a mock application - setApplication(mApplication); - bindService(startIntent); - mAssistant = getService(); - mAssistant.setNoMan(mNoMan); - mAssistant.setFile(mFile); - when(mFile.startWrite()).thenReturn(mock(FileOutputStream.class)); - } - - private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel, - String tag, String groupKey) { - Notification n = new Notification.Builder(mContext, channel.getId()) - .setContentTitle("foo") - .setGroup(groupKey) - .build(); - - StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 0, tag, uid, uid, n, - UserHandle.SYSTEM, null, 0); - - return sbn; - } - - private Ranking generateRanking(StatusBarNotification sbn, NotificationChannel channel) { - Ranking mockRanking = mock(Ranking.class); - when(mockRanking.getChannel()).thenReturn(channel); - when(mockRanking.getImportance()).thenReturn(channel.getImportance()); - when(mockRanking.getKey()).thenReturn(sbn.getKey()); - when(mockRanking.getOverrideGroupKey()).thenReturn(null); - return mockRanking; - } - - private void almostBlockChannel(String pkg, int uid, NotificationChannel channel) { - for (int i = 0; i < ChannelImpressions.DEFAULT_STREAK_LIMIT; i++) { - dismissBadNotification(pkg, uid, channel, String.valueOf(i)); - } - } - - private void dismissBadNotification(String pkg, int uid, NotificationChannel channel, - String tag) { - StatusBarNotification sbn = generateSbn(pkg, uid, channel, tag, null); - mAssistant.setFakeRanking(generateRanking(sbn, channel)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.setFakeRanking(mock(Ranking.class)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - } - - @Test - public void testNoAdjustmentForInitialPost() throws Exception { - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, null, null); - - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - dismissBadNotification(PKG1, UID1, P1C1, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class); - verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture()); - assertEquals(sbn.getKey(), captor.getValue().getKey()); - assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, - captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT)); - } - - @Test - public void testMinCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C3); - dismissBadNotification(PKG1, UID1, P1C3, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "new one!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C3)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testGroupChildCanTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP"); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group"); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class); - verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture()); - assertEquals(sbn.getKey(), captor.getValue().getKey()); - assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, - captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT)); - } - - @Test - public void testGroupSummaryCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP"); - sbn.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY; - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group"); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testAodCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testInteractedCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - stats.setExpanded(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testAppDismissedCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testAppSeparation() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - dismissBadNotification(PKG1, UID1, P1C1, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG2, UID2, P2C1, "new app!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P2C1)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testChannelSeparation() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - dismissBadNotification(PKG1, UID1, P1C1, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C2, "new app!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C2)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testReadXml() throws Exception { - String key1 = mAssistant.getKey("pkg1", 1, "channel1"); - int streak1 = 2; - int views1 = 5; - int dismiss1 = 9; - - int streak1a = 3; - int views1a = 10; - int dismiss1a = 99; - String key1a = mAssistant.getKey("pkg1", 1, "channel1a"); - - int streak2 = 7; - int views2 = 77; - int dismiss2 = 777; - String key2 = mAssistant.getKey("pkg2", 2, "channel2"); - - String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<assistant version=\"1\">\n" - + "<impression-set key=\"" + key1 + "\" " - + "dismisses=\"" + dismiss1 + "\" views=\"" + views1 - + "\" streak=\"" + streak1 + "\"/>\n" - + "<impression-set key=\"" + key1a + "\" " - + "dismisses=\"" + dismiss1a + "\" views=\"" + views1a - + "\" streak=\"" + streak1a + "\"/>\n" - + "<impression-set key=\"" + key2 + "\" " - + "dismisses=\"" + dismiss2 + "\" views=\"" + views2 - + "\" streak=\"" + streak2 + "\"/>\n" - + "</assistant>\n"; - mAssistant.readXml(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes()))); - - ChannelImpressions c1 = mAssistant.getImpressions(key1); - assertEquals(2, c1.getStreak()); - assertEquals(5, c1.getViews()); - assertEquals(9, c1.getDismissals()); - - ChannelImpressions c1a = mAssistant.getImpressions(key1a); - assertEquals(3, c1a.getStreak()); - assertEquals(10, c1a.getViews()); - assertEquals(99, c1a.getDismissals()); - - ChannelImpressions c2 = mAssistant.getImpressions(key2); - assertEquals(7, c2.getStreak()); - assertEquals(77, c2.getViews()); - assertEquals(777, c2.getDismissals()); - } - - @Test - public void testRoundTripXml() throws Exception { - String key1 = mAssistant.getKey("pkg1", 1, "channel1"); - ChannelImpressions ci1 = new ChannelImpressions(); - String key2 = mAssistant.getKey("pkg1", 1, "channel2"); - ChannelImpressions ci2 = new ChannelImpressions(); - for (int i = 0; i < 3; i++) { - ci2.incrementViews(); - ci2.incrementDismissals(); - } - ChannelImpressions ci3 = new ChannelImpressions(); - String key3 = mAssistant.getKey("pkg3", 3, "channel2"); - for (int i = 0; i < 9; i++) { - ci3.incrementViews(); - if (i % 3 == 0) { - ci3.incrementDismissals(); - } - } - - mAssistant.insertImpressions(key1, ci1); - mAssistant.insertImpressions(key2, ci2); - mAssistant.insertImpressions(key3, ci3); - - XmlSerializer serializer = new FastXmlSerializer(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); - mAssistant.writeXml(serializer); - - Assistant assistant = new Assistant(); - assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray()))); - - assertEquals(ci1, assistant.getImpressions(key1)); - assertEquals(ci2, assistant.getImpressions(key2)); - assertEquals(ci3, assistant.getImpressions(key3)); - } - - @Test - public void testSettingsProviderUpdate() { - ContentResolver resolver = mApplication.getContentResolver(); - - // Set up channels - String key = mAssistant.getKey("pkg1", 1, "channel1"); - ChannelImpressions ci = new ChannelImpressions(); - for (int i = 0; i < 3; i++) { - ci.incrementViews(); - if (i % 2 == 0) { - ci.incrementDismissals(); - } - } - - mAssistant.insertImpressions(key, ci); - - // With default values, the blocking helper shouldn't be triggered. - assertEquals(false, ci.shouldTriggerBlock()); - - // Update settings values. - float newDismissToViewRatioLimit = 0f; - int newStreakLimit = 0; - Settings.Global.putFloat(resolver, - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, - newDismissToViewRatioLimit); - Settings.Global.putInt(resolver, - Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit); - - // Notify for the settings values we updated. - resolver.notifyChange( - Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT), null); - resolver.notifyChange( - Settings.Global.getUriFor( - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT), - null); - - // With the new threshold, the blocking helper should be triggered. - assertEquals(true, ci.shouldTriggerBlock()); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java deleted file mode 100644 index 3253802bec03..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.notification; - -import static android.ext.services.notification.ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT; -import static android.ext.services.notification.ChannelImpressions.DEFAULT_STREAK_LIMIT; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class ChannelImpressionsTest { - - @Test - public void testNoResultNoBlock() { - ChannelImpressions ci = new ChannelImpressions(); - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testNoStreakNoBlock() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i < DEFAULT_STREAK_LIMIT - 1; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - } - - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testNoStreakNoBlock_breakStreak() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - if (i == DEFAULT_STREAK_LIMIT - 1) { - ci.resetStreak(); - } - } - - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testStreakBlock() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - } - - assertTrue(ci.shouldTriggerBlock()); - } - - @Test - public void testRatio_NoBlockEvenWithStreak() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - ci.incrementViews(); - } - - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testAppend() { - ChannelImpressions ci = new ChannelImpressions(); - ci.incrementViews(); - ci.incrementDismissals(); - - ChannelImpressions ci2 = new ChannelImpressions(); - ci2.incrementViews(); - ci2.incrementDismissals(); - ci2.incrementViews(); - - ci.append(ci2); - assertEquals(3, ci.getViews()); - assertEquals(2, ci.getDismissals()); - assertEquals(2, ci.getStreak()); - - assertEquals(2, ci2.getViews()); - assertEquals(1, ci2.getDismissals()); - assertEquals(1, ci2.getStreak()); - - // no crash - ci.append(null); - } - - @Test - public void testUpdateThresholds_streakLimitsCorrectlyApplied() { - int updatedStreakLimit = DEFAULT_STREAK_LIMIT + 3; - ChannelImpressions ci = new ChannelImpressions(); - ci.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit); - - for (int i = 0; i <= updatedStreakLimit; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - } - - ChannelImpressions ci2 = new ChannelImpressions(); - ci2.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit); - - for (int i = 0; i < updatedStreakLimit; i++) { - ci2.incrementViews(); - ci2.incrementDismissals(); - } - - assertTrue(ci.shouldTriggerBlock()); - assertFalse(ci2.shouldTriggerBlock()); - } - - @Test - public void testUpdateThresholds_ratioLimitsCorrectlyApplied() { - float updatedDismissRatio = .99f; - ChannelImpressions ci = new ChannelImpressions(); - ci.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT); - - // N views, N-1 dismissals, which doesn't satisfy the ratio = 1 criteria. - for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - if (i != DEFAULT_STREAK_LIMIT) { - ci.incrementDismissals(); - } - } - - ChannelImpressions ci2 = new ChannelImpressions(); - ci2.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT); - - for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) { - ci2.incrementViews(); - ci2.incrementDismissals(); - } - - assertFalse(ci.shouldTriggerBlock()); - assertTrue(ci2.shouldTriggerBlock()); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java deleted file mode 100644 index df4738ff1948..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.storage; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.when; - -import android.app.usage.CacheQuotaHint; -import android.app.usage.UsageStats; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.test.ServiceTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class CacheQuotaServiceImplTest extends ServiceTestCase<CacheQuotaServiceImpl> { - private static final String sTestVolUuid = "uuid"; - private static final String sSecondTestVolUuid = "otherUuid"; - - @Mock private Context mContext; - @Mock private File mFile; - @Mock private VolumeInfo mVolume; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) private StorageManager mStorageManager; - - public CacheQuotaServiceImplTest() { - super(CacheQuotaServiceImpl.class); - } - - @Before - public void setUp() throws Exception { - super.setUp(); - MockitoAnnotations.initMocks(this); - mContext = Mockito.spy(new ContextWrapper(getSystemContext())); - setContext(mContext); - when(mContext.getSystemService(Context.STORAGE_SERVICE)).thenReturn(mStorageManager); - - when(mFile.getUsableSpace()).thenReturn(10000L); - when(mVolume.getPath()).thenReturn(mFile); - when(mStorageManager.findVolumeByUuid(sTestVolUuid)).thenReturn(mVolume); - when(mStorageManager.findVolumeByUuid(sSecondTestVolUuid)).thenReturn(mVolume); - - Intent intent = new Intent(getContext(), CacheQuotaServiceImpl.class); - startService(intent); - } - - @Test - public void testNoApps() { - CacheQuotaServiceImpl service = getService(); - assertEquals(service.onComputeCacheQuotaHints(new ArrayList()).size(), 0); - } - - @Test - public void testOneApp() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - CacheQuotaHint request = makeNewRequest("com.test", sTestVolUuid, 1001, 100L); - requests.add(request); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(1); - assertThat(output.get(0).getQuota()).isEqualTo(1500L); - } - - @Test - public void testTwoAppsOneVolume() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sTestVolUuid, 1002, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - // Note that the sizes are just the cache area split up. - assertThat(output).hasSize(2); - assertThat(output.get(0).getQuota()).isEqualTo(883); - assertThat(output.get(1).getQuota()).isEqualTo(1500 - 883); - } - - @Test - public void testTwoAppsTwoVolumes() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1002, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(2); - assertThat(output.get(0).getQuota()).isEqualTo(1500); - assertThat(output.get(1).getQuota()).isEqualTo(1500); - } - - @Test - public void testMultipleAppsPerUidIsCollated() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sTestVolUuid, 1001, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(1); - assertThat(output.get(0).getQuota()).isEqualTo(1500); - } - - @Test - public void testTwoAppsTwoVolumesTwoUuidsShouldBESeparate() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1001, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(2); - assertThat(output.get(0).getQuota()).isEqualTo(1500); - assertThat(output.get(1).getQuota()).isEqualTo(1500); - } - - private CacheQuotaHint makeNewRequest(String packageName, String uuid, int uid, long foregroundTime) { - UsageStats stats = new UsageStats(); - stats.mPackageName = packageName; - stats.mTotalTimeInForeground = foregroundTime; - return new CacheQuotaHint.Builder() - .setVolumeUuid(uuid).setUid(uid).setUsageStats(stats).setQuota(-1).build(); - } -} |