/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.android.settings.search; import static com.android.settings.slices.SliceDeepLinkSpringBoard.INTENT; import static com.android.settings.slices.SliceDeepLinkSpringBoard.SETTINGS; import android.app.job.JobInfo; import android.app.job.JobScheduler; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.slices.SettingsSliceProvider; import java.util.List; import java.util.Locale; public interface DeviceIndexFeatureProvider { String TAG = "DeviceIndex"; String INDEX_VERSION = "settings:index_version"; String INDEX_LANGUAGE = "settings:language"; // Increment when new items are added to ensure they get pushed to the device index. String VERSION = Build.CUSTOM_FINGERPRINT; // When the device language changes, re-index so Slices trigger in device language. Locale LANGUAGE = Locale.getDefault(); boolean isIndexingEnabled(); void index(Context context, CharSequence title, Uri sliceUri, Uri launchUri, List keywords); void clearIndex(Context context); default void updateIndex(Context context, boolean force) { if (!isIndexingEnabled()) { Log.i(TAG, "Skipping: device index is not enabled"); return; } if (!Utils.isDeviceProvisioned(context)) { Log.w(TAG, "Skipping: device is not provisioned"); return; } final ComponentName jobComponent = new ComponentName(context.getPackageName(), DeviceIndexUpdateJobService.class.getName()); try { final int callerUid = Binder.getCallingUid(); final ServiceInfo si = context.getPackageManager().getServiceInfo(jobComponent, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); if (si == null) { Log.w(TAG, "Skipping: No such service " + jobComponent); return; } if (si.applicationInfo.uid != callerUid) { Log.w(TAG, "Skipping: Uid cannot schedule DeviceIndexUpdate: " + callerUid); return; } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Skipping: error finding DeviceIndexUpdateJobService from packageManager"); return; } if (!force && skipIndex(context)) { Log.i(TAG, "Skipping: already indexed."); // No need to update. return; } // Prevent scheduling multiple jobs setIndexState(context); final int jobId = context.getResources().getInteger(R.integer.device_index_update); // Schedule a job so that we know it'll be able to complete, but try to run as // soon as possible. context.getSystemService(JobScheduler.class).schedule( new JobInfo.Builder(jobId, jobComponent) .setPersisted(true) .setMinimumLatency(1000) .setOverrideDeadline(1) .build()); } static Uri createDeepLink(String s) { return new Uri.Builder().scheme(SETTINGS) .authority(SettingsSliceProvider.SLICE_AUTHORITY) .appendQueryParameter(INTENT, s) .build(); } static boolean skipIndex(Context context) { final boolean isSameVersion = TextUtils.equals( Settings.Secure.getString(context.getContentResolver(), INDEX_VERSION), VERSION); final boolean isSameLanguage = TextUtils.equals( Settings.Secure.getString(context.getContentResolver(), INDEX_LANGUAGE), LANGUAGE.toString()); return isSameLanguage && isSameVersion; } static void setIndexState(Context context) { Settings.Secure.putString(context.getContentResolver(), INDEX_VERSION, VERSION); Settings.Secure.putString(context.getContentResolver(), INDEX_LANGUAGE, LANGUAGE.toString()); } }