Java tutorial
/* * Copyright (C) 2007 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 jackpal.androidterm; import android.app.ActivityOptions; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.IntentSender; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.Rect; import android.net.Uri; import android.os.*; import android.content.Intent; import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import android.app.Notification; import android.app.PendingIntent; import android.support.v4.app.NotificationCompat; import jackpal.androidterm.emulatorview.TermSession; import jackpal.androidterm.compat.ServiceForegroundCompat; import jackpal.androidterm.libtermexec.v1.*; import jackpal.androidterm.util.SessionList; import jackpal.androidterm.util.TermSettings; import java.util.UUID; public class TermService extends Service implements TermSession.FinishCallback { /* Parallels the value of START_STICKY on API Level >= 5 */ private static final int COMPAT_START_STICKY = 1; private static final String KEY_TEXT_REPLY = "key_text_reply"; private static final int RUNNING_NOTIFICATION = 1; private ServiceForegroundCompat compat; private SessionList mTermSessions; public class TSBinder extends Binder { TermService getService() { Log.i("TermService", "Activity binding to service"); return TermService.this; } } private final IBinder mTSBinder = new TSBinder(); @Override public void onStart(Intent intent, int flags) { } /* This should be @Override if building with API Level >=5 */ @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY_COMPATIBILITY; } @Override public IBinder onBind(Intent intent) { if (TermExec.SERVICE_ACTION_V1.equals(intent.getAction())) { Log.i("TermService", "Outside process called onBind()"); return new RBinder(); } else { Log.i("TermService", "Activity called onBind()"); return mTSBinder; } } @Override public void onCreate() { // should really belong to the Application class, but we don't use one... final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); SharedPreferences.Editor editor = prefs.edit(); String defValue = getDir("HOME", MODE_PRIVATE).getAbsolutePath(); String homePath = prefs.getString("home_path", defValue); editor.putString("home_path", homePath); editor.commit(); compat = new ServiceForegroundCompat(this); mTermSessions = new SessionList(); /* Put the service in the foreground. */ // Define the bounds in which the Activity will be launched into. Intent notifyIntent = new Intent(this, Term.class); notifyIntent.addFlags(NotificationCompat.FLAG_ONGOING_EVENT); notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this); builder.setSmallIcon(R.drawable.ic_stat_service_notification_icon); builder.setWhen(System.currentTimeMillis()); Notification simpleNotice = builder.setContentText(getString(R.string.service_notify_text)) .setContentText(getString(R.string.service_notify_text)) .setSmallIcon(R.drawable.ic_stat_service_notification_icon).setContentIntent(pendingIntent) .setAutoCancel(true).setDefaults(Notification.DEFAULT_ALL).setPriority(Notification.PRIORITY_HIGH) .build(); NotificationManager mNotificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE); mNotificationManager.notify(0, simpleNotice); Log.d(TermDebug.LOG_TAG, "TermService started"); return; } @Override public void onDestroy() { compat.stopForeground(true); for (TermSession session : mTermSessions) { /* Don't automatically remove from list of sessions -- we clear the * list below anyway and we could trigger * ConcurrentModificationException if we do */ session.setFinishCallback(null); session.finish(); } mTermSessions.clear(); return; } public SessionList getSessions() { return mTermSessions; } public void onSessionFinish(TermSession session) { mTermSessions.remove(session); } private final class RBinder extends ITerminal.Stub { @Override public IntentSender startSession(final ParcelFileDescriptor pseudoTerminalMultiplexerFd, final ResultReceiver callback) { final String sessionHandle = UUID.randomUUID().toString(); // distinct Intent Uri and PendingIntent requestCode must be sufficient to avoid collisions final Intent switchIntent = new Intent(RemoteInterface.PRIVACT_OPEN_NEW_WINDOW) .setData(Uri.parse(sessionHandle)).addCategory(Intent.CATEGORY_DEFAULT) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(RemoteInterface.PRIVEXTRA_TARGET_WINDOW, sessionHandle); final PendingIntent result = PendingIntent.getActivity(getApplicationContext(), sessionHandle.hashCode(), switchIntent, 0); final PackageManager pm = getPackageManager(); final String[] pkgs = pm.getPackagesForUid(getCallingUid()); if (pkgs == null || pkgs.length == 0) return null; for (String packageName : pkgs) { try { final PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0); final ApplicationInfo appInfo = pkgInfo.applicationInfo; if (appInfo == null) continue; final CharSequence label = pm.getApplicationLabel(appInfo); if (!TextUtils.isEmpty(label)) { final String niceName = label.toString(); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { final TermSettings settings = new TermSettings(getResources(), PreferenceManager.getDefaultSharedPreferences(getApplicationContext())); final GenericTermSession session = new BoundSession(pseudoTerminalMultiplexerFd, settings, niceName); mTermSessions.add(session); session.setHandle(sessionHandle); session.setFinishCallback(new RBinderCleanupCallback(result, callback)); session.setTitle(""); // TODO: handle the situation, when supplied file descriptor does not originate from // /dev/ptmx (probably should be implemented by throwing IllegalStateEXception // when recognized specific errno values in native Exec methods) session.initializeEmulator(80, 24); } }); } return result.getIntentSender(); } catch (PackageManager.NameNotFoundException ignore) { } } return null; } } private final class RBinderCleanupCallback implements TermSession.FinishCallback { private final PendingIntent result; private final ResultReceiver callback; public RBinderCleanupCallback(PendingIntent result, ResultReceiver callback) { this.result = result; this.callback = callback; } @Override public void onSessionFinish(TermSession session) { result.cancel(); callback.send(0, new Bundle()); mTermSessions.remove(session); } } }