1/*
2 * Copyright (C) 2013 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
17package com.android.testing.sleephelper;
18
19import android.app.Activity;
20import android.app.Instrumentation;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.os.Bundle;
26import android.os.Debug;
27import android.os.IBinder;
28import android.os.RemoteException;
29import android.util.Log;
30
31import com.android.testing.alarmservice.Alarm;
32
33public class SetAlarm extends Instrumentation {
34
35    private static final String COMMAND = "command";
36    private static final String PARAM = "param";
37    private static final String CMD_PREPARE = "prepare";
38    private static final String CMD_SET = "set_wait";
39    private static final String CMD_DONE = "done";
40    private static final String SERVICE_ACTION = "com.android.testing.ALARM_SERVICE";
41    private static final String SERVICE_PKG = "com.android.testing.alarmservice";
42    private static final String LOG_TAG = SetAlarm.class.getSimpleName();
43
44    private Alarm mAlarmService = null;
45    private Bundle mArgs = null;
46    private String mCommand = null;
47    private Intent mServceIntent = new Intent(SERVICE_ACTION).setPackage(SERVICE_PKG);
48
49    private ServiceConnection mConn = new ServiceConnection() {
50        @Override
51        public void onServiceDisconnected(ComponentName name) {
52            Log.d(LOG_TAG, "Service disconnected.");
53            mAlarmService = null;
54            errorFinish("service disconnected");
55        }
56
57        @Override
58        public void onServiceConnected(ComponentName name, IBinder service) {
59            Log.d(LOG_TAG, "Service connected.");
60            mAlarmService = Alarm.Stub.asInterface(service);
61            handleCommands();
62        }
63    };
64
65
66    private void handleCommands() {
67        if (CMD_PREPARE.equals(mCommand)) {
68            callPrepare();
69        } else if (CMD_SET.equals(mCommand)) {
70            String paramString = mArgs.getString(PARAM);
71            if (paramString == null) {
72                errorFinish("argument expected for alarm time");
73            }
74            long timeout = -1;
75            try {
76                timeout = Long.parseLong(paramString);
77            } catch (NumberFormatException nfe) {
78                errorFinish("a number argument is expected");
79            }
80            callSetAndWait(timeout);
81        } else if (CMD_DONE.equals(mCommand)) {
82            callDone();
83        } else {
84            errorFinish("Unrecognized command: " + mCommand);
85        }
86        finish(Activity.RESULT_OK, new Bundle());
87    }
88
89    @Override
90    public void onCreate(Bundle arguments) {
91        super.onCreate(arguments);
92        mCommand = arguments.getString(COMMAND);
93        if ("true".equals(arguments.getString("debug"))) {
94            Debug.waitForDebugger();
95        }
96        if (mCommand == null) {
97            errorFinish("No command specified");
98        }
99        mArgs = arguments;
100        connectToAlarmService();
101    }
102
103    private void errorFinish(String msg) {
104        Bundle ret = new Bundle();
105        ret.putString("error", msg);
106        finish(Activity.RESULT_CANCELED, ret);
107    }
108
109    private void connectToAlarmService() {
110        // start the service with an intent, this ensures the service keeps running after unbind
111        ComponentName cn = getContext().startService(mServceIntent);
112        if (cn == null) {
113            errorFinish("failed to start service");
114        }
115        if (!getContext().bindService(mServceIntent, mConn, Context.BIND_AUTO_CREATE)) {
116            errorFinish("failed to bind service");
117        }
118    }
119
120    private void callPrepare() {
121        try {
122            mAlarmService.prepare();
123        } catch (RemoteException e) {
124            errorFinish("RemoteExeption in prepare()");
125        } finally {
126            getContext().unbindService(mConn);
127        }
128    }
129
130    private void callDone() {
131        try {
132            mAlarmService.done();
133        } catch (RemoteException e) {
134            errorFinish("RemoteExeption in prepare()");
135        } finally {
136            getContext().unbindService(mConn);
137        }
138        // explicitly stop the service (started in prepare()) so that the service is now free
139        // to be reclaimed
140        getContext().stopService(mServceIntent);
141    }
142
143    private void callSetAndWait(long timeoutMills) {
144        try {
145            mAlarmService.setAlarmAndWait(timeoutMills);
146        } catch (RemoteException e) {
147            errorFinish("RemoteExeption in setAlarmAndWait()");
148        } finally {
149            getContext().unbindService(mConn);
150        }
151    }
152}
153