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.ArrayList;
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 ArrayList<InterruptMaintenance> sInterruptList = new ArrayList<>();
69     private static IpMemoryStoreService sIpMemoryStoreService;
70 
71     @Override
onStartJob(JobParameters params)72     public boolean onStartJob(JobParameters params) {
73         if (sIpMemoryStoreService == null) {
74             Log.wtf("RegularMaintenanceJobService",
75                     "Can not start job because sIpMemoryStoreService is null.");
76             return false;
77         }
78         final InterruptMaintenance im = new InterruptMaintenance(params.getJobId());
79         sInterruptList.add(im);
80 
81         sIpMemoryStoreService.fullMaintenance(new IOnStatusListener() {
82             @Override
83             public void onComplete(final StatusParcelable statusParcelable) throws RemoteException {
84                 final Status result = new Status(statusParcelable);
85                 if (!result.isSuccess()) {
86                     Log.e("RegularMaintenanceJobService", "Regular maintenance failed."
87                             + " Error is " + result.resultCode);
88                 }
89                 sInterruptList.remove(im);
90                 jobFinished(params, !result.isSuccess());
91             }
92 
93             @Override
94             public int getInterfaceVersion() {
95                 return this.VERSION;
96             }
97 
98             @Override
99             public String getInterfaceHash() {
100                 return this.HASH;
101             }
102 
103             @Override
104             public IBinder asBinder() {
105                 return null;
106             }
107         }, im);
108         return true;
109     }
110 
111     @Override
onStopJob(JobParameters params)112     public boolean onStopJob(JobParameters params) {
113         final int jobId = params.getJobId();
114         for (InterruptMaintenance im : sInterruptList) {
115             if (im.getJobId() == jobId) {
116                 im.setInterrupted(true);
117             }
118         }
119         return true;
120     }
121 
122     /** Schedule regular maintenance job */
schedule(Context context, IpMemoryStoreService ipMemoryStoreService)123     static void schedule(Context context, IpMemoryStoreService ipMemoryStoreService) {
124         final JobScheduler jobScheduler =
125                 (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
126 
127         final ComponentName maintenanceJobName =
128                 new ComponentName(context, RegularMaintenanceJobService.class);
129 
130         // Regular maintenance is scheduled for when the device is idle with access power and a
131         // minimum interval of one day.
132         final JobInfo regularMaintenanceJob =
133                 new JobInfo.Builder(REGULAR_MAINTENANCE_ID, maintenanceJobName)
134                         .setRequiresDeviceIdle(true)
135                         .setRequiresCharging(true)
136                         .setRequiresBatteryNotLow(true)
137                         .setPeriodic(TimeUnit.HOURS.toMillis(24)).build();
138 
139         jobScheduler.schedule(regularMaintenanceJob);
140         sIpMemoryStoreService = ipMemoryStoreService;
141     }
142 
143     /** Unschedule regular maintenance job */
unschedule(Context context)144     static void unschedule(Context context) {
145         final JobScheduler jobScheduler =
146                 (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
147         jobScheduler.cancel(REGULAR_MAINTENANCE_ID);
148         sIpMemoryStoreService = null;
149     }
150 }
151