1 /*
2  * Copyright (C) 2019 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 
17 package com.android.server.connectivity.ipmemorystore;
18 
19 import android.app.job.JobInfo;
20 import android.app.job.JobParameters;
21 import android.app.job.JobScheduler;
22 import android.app.job.JobService;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.net.ipmemorystore.IOnStatusListener;
26 import android.net.ipmemorystore.Status;
27 import android.net.ipmemorystore.StatusParcelable;
28 import android.os.IBinder;
29 import android.os.RemoteException;
30 import android.util.Log;
31 
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import java.util.concurrent.TimeUnit;
34 
35 /**
36  * Regular maintenance job service.
37  * @hide
38  */
39 public final class RegularMaintenanceJobService extends JobService {
40     // Must be unique within the system server uid.
41     public static final int REGULAR_MAINTENANCE_ID = 3345678;
42 
43     /**
44      * Class for interrupt check of maintenance job.
45      */
46     public static final class InterruptMaintenance {
47         private volatile boolean mIsInterrupted;
48         private final int mJobId;
49 
InterruptMaintenance(int jobId)50         public InterruptMaintenance(int jobId) {
51             mJobId = jobId;
52             mIsInterrupted = false;
53         }
54 
getJobId()55         public int getJobId() {
56             return mJobId;
57         }
58 
setInterrupted(boolean interrupt)59         public void setInterrupted(boolean interrupt) {
60             mIsInterrupted = interrupt;
61         }
62 
isInterrupted()63         public boolean isInterrupted() {
64             return mIsInterrupted;
65         }
66     }
67 
68     private static final CopyOnWriteArrayList<InterruptMaintenance> sInterruptList =
69             new CopyOnWriteArrayList<>();
70     private static IpMemoryStoreService sIpMemoryStoreService;
71 
72     @Override
onStartJob(JobParameters params)73     public boolean onStartJob(JobParameters params) {
74         if (sIpMemoryStoreService == null) {
75             Log.wtf("RegularMaintenanceJobService",
76                     "Can not start job because sIpMemoryStoreService is null.");
77             return false;
78         }
79         final InterruptMaintenance im = new InterruptMaintenance(params.getJobId());
80         sInterruptList.add(im);
81 
82         sIpMemoryStoreService.fullMaintenance(new IOnStatusListener() {
83             @Override
84             public void onComplete(final StatusParcelable statusParcelable) throws RemoteException {
85                 final Status result = new Status(statusParcelable);
86                 if (!result.isSuccess()) {
87                     Log.e("RegularMaintenanceJobService", "Regular maintenance failed."
88                             + " Error is " + result.resultCode);
89                 }
90                 sInterruptList.remove(im);
91                 jobFinished(params, !result.isSuccess());
92             }
93 
94             @Override
95             public int getInterfaceVersion() {
96                 return this.VERSION;
97             }
98 
99             @Override
100             public String getInterfaceHash() {
101                 return this.HASH;
102             }
103 
104             @Override
105             public IBinder asBinder() {
106                 return null;
107             }
108         }, im);
109         return true;
110     }
111 
112     @Override
onStopJob(JobParameters params)113     public boolean onStopJob(JobParameters params) {
114         final int jobId = params.getJobId();
115         for (InterruptMaintenance im : sInterruptList) {
116             if (im.getJobId() == jobId) {
117                 im.setInterrupted(true);
118             }
119         }
120         return true;
121     }
122 
123     /** Schedule regular maintenance job */
schedule(Context context, IpMemoryStoreService ipMemoryStoreService)124     static void schedule(Context context, IpMemoryStoreService ipMemoryStoreService) {
125         final JobScheduler jobScheduler =
126                 (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
127 
128         final ComponentName maintenanceJobName =
129                 new ComponentName(context, RegularMaintenanceJobService.class);
130 
131         // Regular maintenance is scheduled for when the device is idle with access power and a
132         // minimum interval of one day.
133         final JobInfo regularMaintenanceJob =
134                 new JobInfo.Builder(REGULAR_MAINTENANCE_ID, maintenanceJobName)
135                         .setRequiresDeviceIdle(true)
136                         .setRequiresCharging(true)
137                         .setRequiresBatteryNotLow(true)
138                         .setPeriodic(TimeUnit.HOURS.toMillis(24)).build();
139 
140         jobScheduler.schedule(regularMaintenanceJob);
141         sIpMemoryStoreService = ipMemoryStoreService;
142     }
143 
144     /** Unschedule regular maintenance job */
unschedule(Context context)145     static void unschedule(Context context) {
146         final JobScheduler jobScheduler =
147                 (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
148         jobScheduler.cancel(REGULAR_MAINTENANCE_ID);
149         sIpMemoryStoreService = null;
150     }
151 }
152