1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.settings.search; 16 17 import static com.android.settings.slices.SliceDeepLinkSpringBoard.INTENT; 18 import static com.android.settings.slices.SliceDeepLinkSpringBoard.SETTINGS; 19 20 import android.app.job.JobInfo; 21 import android.app.job.JobScheduler; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ServiceInfo; 26 import android.net.Uri; 27 import android.os.Binder; 28 import android.os.Build; 29 import android.provider.Settings; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import com.android.settings.R; 34 import com.android.settings.Utils; 35 import com.android.settings.slices.SettingsSliceProvider; 36 37 import java.util.List; 38 import java.util.Locale; 39 40 public interface DeviceIndexFeatureProvider { 41 42 String TAG = "DeviceIndex"; 43 44 String INDEX_VERSION = "settings:index_version"; 45 String INDEX_LANGUAGE = "settings:language"; 46 47 // Increment when new items are added to ensure they get pushed to the device index. 48 String VERSION = Build.FINGERPRINT; 49 50 // When the device language changes, re-index so Slices trigger in device language. 51 Locale LANGUAGE = Locale.getDefault(); 52 isIndexingEnabled()53 boolean isIndexingEnabled(); 54 index(Context context, CharSequence title, Uri sliceUri, Uri launchUri, List<String> keywords)55 void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri, 56 List<String> keywords); 57 clearIndex(Context context)58 void clearIndex(Context context); 59 updateIndex(Context context, boolean force)60 default void updateIndex(Context context, boolean force) { 61 if (!isIndexingEnabled()) { 62 Log.i(TAG, "Skipping: device index is not enabled"); 63 return; 64 } 65 66 if (!Utils.isDeviceProvisioned(context)) { 67 Log.w(TAG, "Skipping: device is not provisioned"); 68 return; 69 } 70 71 final ComponentName jobComponent = new ComponentName(context.getPackageName(), 72 DeviceIndexUpdateJobService.class.getName()); 73 74 try { 75 final int callerUid = Binder.getCallingUid(); 76 final ServiceInfo si = context.getPackageManager().getServiceInfo(jobComponent, 77 PackageManager.MATCH_DIRECT_BOOT_AWARE 78 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 79 if (si == null) { 80 Log.w(TAG, "Skipping: No such service " + jobComponent); 81 return; 82 } 83 if (si.applicationInfo.uid != callerUid) { 84 Log.w(TAG, "Skipping: Uid cannot schedule DeviceIndexUpdate: " + callerUid); 85 return; 86 } 87 } catch (PackageManager.NameNotFoundException e) { 88 Log.w(TAG, "Skipping: error finding DeviceIndexUpdateJobService from packageManager"); 89 return; 90 } 91 92 if (!force && skipIndex(context)) { 93 Log.i(TAG, "Skipping: already indexed."); 94 // No need to update. 95 return; 96 } 97 98 // Prevent scheduling multiple jobs 99 setIndexState(context); 100 101 final int jobId = context.getResources().getInteger(R.integer.device_index_update); 102 // Schedule a job so that we know it'll be able to complete, but try to run as 103 // soon as possible. 104 context.getSystemService(JobScheduler.class).schedule( 105 new JobInfo.Builder(jobId, jobComponent) 106 .setPersisted(true) 107 .setMinimumLatency(1000) 108 .setOverrideDeadline(1) 109 .build()); 110 111 } 112 createDeepLink(String s)113 static Uri createDeepLink(String s) { 114 return new Uri.Builder().scheme(SETTINGS) 115 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 116 .appendQueryParameter(INTENT, s) 117 .build(); 118 } 119 skipIndex(Context context)120 static boolean skipIndex(Context context) { 121 final boolean isSameVersion = TextUtils.equals( 122 Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), VERSION); 123 final boolean isSameLanguage = TextUtils.equals( 124 Settings.Secure.getString(context.getContentResolver(), INDEX_LANGUAGE), 125 LANGUAGE.toString()); 126 return isSameLanguage && isSameVersion; 127 } 128 setIndexState(Context context)129 static void setIndexState(Context context) { 130 Settings.Secure.putString(context.getContentResolver(), INDEX_VERSION, VERSION); 131 Settings.Secure.putString(context.getContentResolver(), INDEX_LANGUAGE, 132 LANGUAGE.toString()); 133 } 134 } 135