platform-packages-apps-Settings / src / com / android / settings / search / DeviceIndexFeatureProvider.java
DeviceIndexFeatureProvider.java
Raw
/*
 * 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<String> 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());
    }
}