1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settingslib.schedulesprovider; 17 18 import android.content.ContentProvider; 19 import android.content.ContentValues; 20 import android.database.Cursor; 21 import android.net.Uri; 22 import android.os.Bundle; 23 import android.os.SystemProperties; 24 import android.text.TextUtils; 25 import android.util.Log; 26 27 import androidx.annotation.NonNull; 28 import androidx.annotation.Nullable; 29 30 import java.util.ArrayList; 31 import java.util.stream.Collectors; 32 33 /** 34 * A bridge for client apps to provide the schedule data. Client provider needs to implement 35 * {@link #getScheduleInfoList()} returning a list of {@link ScheduleInfo}. 36 */ 37 public abstract class SchedulesProvider extends ContentProvider { 38 public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList"; 39 public static final String BUNDLE_SCHEDULE_INFO_LIST = "scheduleInfoList"; 40 private static final String TAG = "SchedulesProvider"; 41 42 @Override onCreate()43 public boolean onCreate() { 44 return true; 45 } 46 47 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)48 public final Cursor query(Uri uri, String[] projection, String selection, 49 String[] selectionArgs, String sortOrder) { 50 throw new UnsupportedOperationException("Query operation is not supported currently."); 51 } 52 53 @Override getType(Uri uri)54 public final String getType(Uri uri) { 55 throw new UnsupportedOperationException("GetType operation is not supported currently."); 56 } 57 58 @Override insert(Uri uri, ContentValues values)59 public final Uri insert(Uri uri, ContentValues values) { 60 throw new UnsupportedOperationException("Insert operation is not supported currently."); 61 } 62 63 @Override delete(Uri uri, String selection, String[] selectionArgs)64 public final int delete(Uri uri, String selection, String[] selectionArgs) { 65 throw new UnsupportedOperationException("Delete operation not supported currently."); 66 } 67 68 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)69 public final int update(Uri uri, ContentValues values, String selection, 70 String[] selectionArgs) { 71 throw new UnsupportedOperationException("Update operation is not supported currently."); 72 } 73 74 /** 75 * Returns the list of the schedule information. 76 */ getScheduleInfoList()77 public abstract ArrayList<ScheduleInfo> getScheduleInfoList(); 78 79 /** 80 * Returns a bundle which contains a list of {@link ScheduleInfo}s: 81 * 82 * <ul> 83 * <li>scheduleInfoList: ArrayList<ScheduleInfo> 84 * </ul> 85 */ 86 @Override call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)87 public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { 88 if (!TextUtils.equals(getCallingPackage(), 89 getContext().getText(R.string.config_schedules_provider_caller_package))) { 90 return null; 91 } 92 93 final Bundle bundle = new Bundle(); 94 if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) { 95 final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData( 96 getScheduleInfoList()); 97 if (scheduleInfoList != null) { 98 bundle.putParcelableArrayList(BUNDLE_SCHEDULE_INFO_LIST, scheduleInfoList); 99 } 100 } 101 return bundle; 102 } 103 104 /** 105 * Filters our invalid schedule infos from {@code schedulesInfoList}. 106 * 107 * @return valid {@link SchedulesInfo}s if {@code schedulesInfoList} is not null. Otherwise, 108 * null. 109 */ 110 @Nullable filterInvalidData( @ullable ArrayList<ScheduleInfo> scheduleInfoList)111 private ArrayList<ScheduleInfo> filterInvalidData( 112 @Nullable ArrayList<ScheduleInfo> scheduleInfoList) { 113 if (scheduleInfoList == null) { 114 Log.d(TAG, "package : " + getContext().getPackageName() + " has no scheduling data."); 115 return null; 116 } 117 // Dump invalid data in debug mode. 118 if (SystemProperties.getInt("ro.debuggable", 0) == 1) { 119 dumpInvalidData(scheduleInfoList); 120 } 121 return scheduleInfoList 122 .stream() 123 .filter(ScheduleInfo::isValid) 124 .collect(Collectors.toCollection(ArrayList::new)); 125 } 126 dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList)127 private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) { 128 final boolean hasInvalidData = scheduleInfoList 129 .stream() 130 .anyMatch(scheduleInfo -> !scheduleInfo.isValid()); 131 132 if (hasInvalidData) { 133 Log.w(TAG, "package : " + getContext().getPackageName() 134 + " provided some scheduling data that are invalid."); 135 scheduleInfoList 136 .stream() 137 .filter(scheduleInfo -> !scheduleInfo.isValid()) 138 .forEach(scheduleInfo -> Log.w(TAG, scheduleInfo.toString())); 139 } 140 } 141 } 142