com.orange.oidc.tim.service.Service.java Source code

Java tutorial

Introduction

Here is the source code for com.orange.oidc.tim.service.Service.java

Source

/*
* 
* Copyright (C) 2015 Orange Labs
* 
* 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 com.orange.oidc.tim.service;

import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

import org.json.JSONException;
import org.json.JSONObject;

import org.simalliance.openmobileapi.*;

import com.orange.oidc.tim.service.IRemoteListenerToken;
import com.orange.oidc.tim.service.IRemoteService;
import com.orange.oidc.tim.service.IRemoteServiceInternal;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;

/**
 * Openid Connect proxy service class
 *
 */
public class Service extends android.app.Service implements SEService.CallBack {

    protected static final String TAG = "Service";

    final static String EMPTY = "";

    static {
        Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
    }

    int idList = 0;

    class RemoteListenerToken {
        IRemoteListenerToken listener;
        String id;
        boolean useTim;
        OpenidConnectParams ocp;

        RemoteListenerToken(IRemoteListenerToken r) {
            listener = r;
            idList++;
            id = "" + idList;
        }
    };

    List<RemoteListenerToken> RemoteListenerTokenList = new ArrayList<RemoteListenerToken>();

    public static Service theService = null;

    private static TimSecureStorage secureStorage;

    public Service() {
        // android.os.Debug.waitForDebugger();
        System.setProperty("http.keepAlive", "false");
        if (theService == null) {
            theService = this;
            // init secure storage
            secureStorage = new SDCardStorage();
            // secureStorage = new SIMStorage();

            // init Http
            HttpOpenidConnect.secureStorage = secureStorage;
        }
    }

    // private final IRemoteServiceBinder mBinder = new IRemoteServiceBinder();

    public class ServiceBinder extends Binder {
        Service getService() {
            return Service.this;
        }
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {

        @Override
        public void getTokensWithTim(IRemoteListenerToken listener, String serverUrl, String client_id,
                String scope, String state, String nonce) throws RemoteException {

            Logd(TAG, "getTokensWithTim begin");

            showNotProtectedNotifIcon();

            if (!serverUrl.endsWith("/"))
                serverUrl += "/";

            scope = sortScope(scope + " tim");

            OpenidConnectParams ocp = new OpenidConnectParams(serverUrl, client_id, scope,
                    secureStorage.getRedirectUri(), state, nonce, null, null, null);

            Logd(TAG, "ocp: " + ocp.toString());

            RemoteListenerToken rl;
            synchronized (RemoteListenerTokenList) {
                rl = new RemoteListenerToken(listener);
                rl.ocp = ocp;
                rl.useTim = true;
                RemoteListenerTokenList.add(rl);
            }

            // check if tokens present
            try {

                TokensKeys tk = null;
                if (secureStorage != null)
                    tk = secureStorage.read_tokens(serverUrl, client_id, scope + " tim");

                if (tk != null) {
                    // check validity of refresh token
                    Log.d(TAG, "refresh token found, validity check begin");
                    Log.d(TAG, "expiration du token : " + tk.getExpires());
                    if (tk.isRefreshTokenValid()) {
                        // refresh token keys
                        Logd(TAG, "getTokensWithTim refreshTokenWithTim");
                        String refreshResult = refreshTokenWithTim(serverUrl, client_id, scope);
                        Logd(TAG, "getTokensWithTim refresh result: ");
                        Logd(TAG, "" + refreshResult);
                        setClientTokens(rl.id, refreshResult);
                        Logd(TAG, "getTokensWithTim refreshTokenWithTim end");
                        return;
                    }
                } else if (tk == null) {
                    // for test only
                    // return;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            // every tokens expired, make a new request
            HttpOpenidConnect hc = new HttpOpenidConnect(ocp);

            if (!hc.getTokens(Service.this, rl.id, true, null)) {
                setClientTokens(rl.id, null);
            }

            Logd(TAG, "getTokensWithTim end");

        }

        @Override
        public String getTimUserInfo(String serverUrl, String tim_access_token) throws RemoteException {
            Logd(TAG, "getTimUserInfo");
            showProtectedNotifIcon();
            String result = HttpOpenidConnect.getTimUserInfo(serverUrl, tim_access_token);
            // hideNotifIcon();
            return result;
        }

        @Override
        public String refreshTokenWithTim(String serverUrl, String client_id, String scope) {
            Logd(TAG, "refreshTokenWithTim");

            showNotProtectedNotifIcon();

            scope = sortScope(scope + " tim");

            if (!serverUrl.endsWith("/"))
                serverUrl += "/";
            OpenidConnectParams ocp = new OpenidConnectParams(serverUrl, client_id, scope,
                    secureStorage.getRedirectUri());
            HttpOpenidConnect hc = new HttpOpenidConnect(ocp);

            try {
                String response = hc.refreshTokenWithTIM();
                Logd(TAG, "refreshTokenWithTim response: " + response);
                if (response != null) {
                    JSONObject json = new JSONObject(response);
                    String id_token = getFromJS(json, "id_token");
                    String refresh_token = getFromJS(json, "refresh_token");
                    ocp.m_server_scope = getFromJS(json, "scope");

                    JSONObject retJSon = new JSONObject();
                    retJSon.put("id_token", id_token);

                    String expires = TimSecureStorage.convertExpiresIn(getFromJS(json, "expires_in"));
                    // save new tokens
                    secureStorage.update_tokens(ocp, id_token, refresh_token, expires);
                    retJSon.put("tim_access_token", secureStorage.getNewTimToken(ocp));

                    // hideNotifIcon();
                    return retJSon.toString();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            // hideNotifIcon();
            return null;
        }

        @Override
        public String getNewTimToken(String serverUrl, String client_id, String scope) {
            Logd(TAG, "getNewTimToken");
            showProtectedNotifIcon();

            scope = sortScope(scope + " tim");

            if (!serverUrl.endsWith("/"))
                serverUrl += "/";

            OpenidConnectParams ocp = new OpenidConnectParams(serverUrl, client_id, scope, null);

            String newTimToken = secureStorage.getNewTimToken(ocp);

            // hideNotifIcon();

            return newTimToken;
        }

        @Override
        public boolean deleteTokens(String serverUrl, String client_id, String scope) {
            Logd(TAG, "deleteTokens " + client_id);

            resetCookies();

            scope = sortScope(scope);
            if (!serverUrl.endsWith("/"))
                serverUrl += "/";

            return secureStorage.delete_tokens(serverUrl, client_id, scope);
        }

        @Override
        public String webFinger(String userInput, String serverUrl) {
            try {
                return HttpOpenidConnect.webfinger(userInput, serverUrl);
            } catch (Exception e) {

            }
            return null;
        }

        /** openid connect only methods
         */
        @Override
        public void getTokens(IRemoteListenerToken listener, String serverUrl, String client_id,
                String client_secret, String scope, String redirect_uri, String state, String nonce) {

            Logd(TAG, "getTokens begin");

            scope = sortScope(scope);
            if (!serverUrl.endsWith("/"))
                serverUrl += "/";

            OpenidConnectParams ocp = new OpenidConnectParams();
            ocp.init(serverUrl, client_id, scope, redirect_uri, state, nonce, null, client_secret, null);

            RemoteListenerToken rl;
            synchronized (RemoteListenerTokenList) {
                rl = new RemoteListenerToken(listener);
                rl.ocp = ocp;
                RemoteListenerTokenList.add(rl);
            }

            HttpOpenidConnect hc = new HttpOpenidConnect(ocp);

            if (!hc.getTokens(Service.this, rl.id, false, null)) {
                setClientTokens(rl.id, null);
            }

            Logd(TAG, "getTokens end");
        }

        @Override
        public String refreshToken(String serverUrl, String client_id, String client_secret, String scope,
                String redirect_uri, String refresh_token) {

            scope = sortScope(scope);
            if (!serverUrl.endsWith("/"))
                serverUrl += "/";
            OpenidConnectParams ocp = new OpenidConnectParams(serverUrl, client_id, scope, redirect_uri);
            HttpOpenidConnect hc = new HttpOpenidConnect(ocp);

            try {
                String response = hc.refreshToken(refresh_token);
                Logd(TAG, "refreshToken response: " + response);
                if (response != null) {
                    return response;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public String getUserInfo(String serverUrl, String access_token) {
            return HttpOpenidConnect.getUserInfo(serverUrl, access_token);
        }

        // Logout from the idp
        @Override
        public void logout(String serverUrl) {
            HttpOpenidConnect.logout(serverUrl, null);
        }

        /*
         * (non-Javadoc)
         * @see com.orange.openid_connect_php.IRemoteService#logout(java.lang.String)
         * Logout from TIM, IdP and Cloud service
         */
        @Override
        public void revokeLogoutWithTim(String serverUrl, String client_id, String scope) {

            // android.os.Debug.waitForDebugger();

            if (!serverUrl.endsWith("/"))
                serverUrl += "/";
            scope = sortScope(scope + " tim");
            ;

            // search for TokenKey
            TokensKeys tk = secureStorage.read_tokens(serverUrl, client_id, scope);
            if (tk == null || tk.jwkPub == null || tk.jwkPub.length() == 0) {
                // bad parameter, no tim app key found
                return;
            }

            // build custom tim access token with tim app key
            OpenidConnectParams ocp = new OpenidConnectParams(serverUrl, client_id, scope, null);
            ocp.m_jwk = tk.jwkPub;

            if (HttpOpenidConnect.logout(serverUrl, secureStorage.getNewTimToken(ocp))) {
                Logd(TAG, "Online deletion OK");
            }

            Logd(TAG, "Deleting local information");
            secureStorage.delete_tokens(serverUrl, client_id, scope);
            resetCookies();
        }

    };

    void resetCookies() {
        // init intent
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setClass(Service.this, WebViewActivity.class);

        intent.putExtra("resetcookies", "true");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP
                | Intent.FLAG_ACTIVITY_NO_ANIMATION);

        // launch webview
        startActivity(intent);
    }

    long getSecretPathThreshold() {
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        long l = sharedPrefs.getLong("threshold", 80);
        return l;
    }

    void setSecretPathThreshold(long val) {
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        Editor editor = sharedPrefs.edit();
        editor.putLong("threshold", val);
        editor.commit();
        Logd(TAG, "threshold: " + val);
    }

    // internal remote service for internal webview
    private final IRemoteServiceInternal.Stub mBinderInternal = new IRemoteServiceInternal.Stub() {

        private void setTokensRedirect(String id, HttpOpenidConnect hc, String redirect) {
            // android.os.Debug.waitForDebugger();

            // check caller uid
            if (checkCallingUid() == false) {
                // caller are not from inside this app
                Logd(TAG, "setTokensRedirect not from inside this app");
                return;
            }

            RemoteListenerToken rl = null;
            synchronized (RemoteListenerTokenList) {
                for (int i = RemoteListenerTokenList.size() - 1; i >= 0; i--) {
                    RemoteListenerToken r = RemoteListenerTokenList.get(i);
                    if (r.id.compareTo(id) == 0) {
                        rl = r;
                        RemoteListenerTokenList.remove(i);
                        break;
                    }
                }
            }

            if (rl != null) {
                try {
                    String tokens = hc.doRedirect(redirect, rl.useTim);

                    if (rl.useTim) {
                        JSONObject jObject = new JSONObject(tokens);

                        boolean user_cancel = false;
                        String userCancel = getFromJS(jObject, "cancel");
                        if (userCancel != null && userCancel.equalsIgnoreCase("true")) {
                            user_cancel = true;
                        }

                        // put id_token and refresh_token in Secure Storage
                        if (user_cancel == false) {
                            // String access_token  = getFromJS( jObject, "access_token" );
                            // String token_type    = getFromJS( jObject, "token_type" );
                            String refresh_token = getFromJS(jObject, "refresh_token");
                            String expires_in = getFromJS(jObject, "expires_in");
                            String id_token = getFromJS(jObject, "id_token");
                            rl.ocp.m_server_scope = getFromJS(jObject, "scope");

                            secureStorage.save_tokens(rl.ocp, id_token, refresh_token, expires_in);

                            // HACK : not nice ... :(
                            hc.mOcp.m_redirect_uri = null;

                            // do now the refresh
                            String refreshResult = hc.refreshTokenWithTIM();
                            if (refreshResult != null) {
                                if (refreshResult != null) {
                                    JSONObject json = new JSONObject(refreshResult);
                                    id_token = getFromJS(json, "id_token");
                                    refresh_token = getFromJS(json, "refresh_token");
                                    rl.ocp.m_server_scope = getFromJS(json, "scope");

                                    String tim_token = null;
                                    if (secureStorage != null) {
                                        String expires = TimSecureStorage
                                                .convertExpiresIn(getFromJS(json, "expires_in"));
                                        // save new tokens
                                        secureStorage.update_tokens(hc.mOcp, id_token, refresh_token, expires);
                                        tim_token = secureStorage.getNewTimToken(hc.mOcp);
                                    }
                                    rl.listener.handleTokenResponseWithTim(id_token, tim_token, false);
                                    return;
                                }
                            }
                        } else {
                            rl.listener.handleTokenResponseWithTim(EMPTY, EMPTY, true);
                            return;
                        }

                    } else {
                        rl.listener.handleTokenResponse(tokens);
                        return;
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }

                // if here, then nothing to notify
                if (rl.listener != null) {
                    try {
                        if (rl.useTim)
                            rl.listener.handleTokenResponseWithTim(EMPTY, EMPTY, false);
                        else
                            rl.listener.handleTokenResponse(EMPTY);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

        @Override
        public void setTokens(String id, String tokens) throws RemoteException {

            // check caller uid
            if (checkCallingUid() == false) {
                // caller are not from inside this app
                Logd(TAG, "setTokens not from inside this app");
                return;
            }

            RemoteListenerToken rl = null;
            synchronized (RemoteListenerTokenList) {
                for (int i = RemoteListenerTokenList.size() - 1; i >= 0; i--) {
                    RemoteListenerToken r = RemoteListenerTokenList.get(i);
                    if (r.id.compareTo(id) == 0) {
                        rl = r;
                        RemoteListenerTokenList.remove(i);
                        break;
                    }
                }
            }

            if (rl != null) {

                JSONObject jObject = null;
                try {
                    if (tokens != null)
                        jObject = new JSONObject(tokens);

                    // String access_token   = getFromJS( jObject, "access_token" );
                    // String token_type    = getFromJS( jObject, "token_type" );
                    String refresh_token = getFromJS(jObject, "refresh_token");
                    String expires_in = getFromJS(jObject, "expires_in");
                    String id_token = getFromJS(jObject, "id_token");
                    rl.ocp.m_server_scope = getFromJS(jObject, "scope");
                    boolean user_cancel = false;
                    String userCancel = getFromJS(jObject, "cancel");
                    if (userCancel != null && userCancel.equalsIgnoreCase("true")) {
                        user_cancel = true;
                    }

                    // put id_token and refresh_token in Secure Storage
                    if (rl.useTim) {
                        String tim_token = null;
                        if (user_cancel == false) {
                            secureStorage.save_tokens(rl.ocp, id_token, refresh_token, expires_in);
                            tim_token = secureStorage.getNewTimToken(rl.ocp);
                        }

                        rl.listener.handleTokenResponseWithTim(id_token, tim_token, false);
                    } else {
                        rl.listener.handleTokenResponse(tokens);
                    }

                    // hideNotifIcon();
                    return;
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            if (rl != null && rl.listener != null) {
                if (rl.useTim)
                    rl.listener.handleTokenResponseWithTim(EMPTY, EMPTY, false);
                else
                    rl.listener.handleTokenResponse(EMPTY);
            }

            // hideNotifIcon();
        }

        @Override
        public void doRedirect(String id, String redirect) {

            // check caller uid
            if (checkCallingUid() == false) {
                // caller are not from inside this app
                Logd(TAG, "doRedirect not from inside this app");
                return;
            }

            Logd(TAG, "doRedirect begin");

            if (id == null || id.length() == 0) {
                Logd(TAG, "doRedirect end no ID");
                // hideNotifIcon();
                return;
            }

            OpenidConnectParams ocp = null;
            // android.os.Debug.waitForDebugger();

            synchronized (RemoteListenerTokenList) {
                for (int i = RemoteListenerTokenList.size() - 1; i >= 0; i--) {
                    RemoteListenerToken r = RemoteListenerTokenList.get(i);
                    if (r.id.compareTo(id) == 0) {
                        ocp = new OpenidConnectParams(r.ocp);
                        break;
                    }
                }
            }

            if (ocp != null) {
                HttpOpenidConnect hc = new HttpOpenidConnect(ocp);

                try {
                    setTokensRedirect(id, hc, redirect);
                    return;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            // if error, set null response
            try {
                setTokens(id, null);
            } catch (Exception e) {
                e.printStackTrace();
            }

            Logd(TAG, "doRedirect end");
            // hideNotifIcon();
        }

        @Override
        public void resetCookies() throws RemoteException {

            // check caller uid
            if (checkCallingUid() == false) {
                // caller are not from inside this app
                Logd(TAG, "resetCookies not from inside this app");
                return;
            }

            // clear cookies
            // android.webkit.CookieManager.getInstance().removeAllCookie();
        }

        // check calling process uid, if different return false
        // possibility of hack
        private boolean checkCallingUid() {
            // check uid
            if (android.os.Process.myUid() == Binder.getCallingUid()) {
                return true;
            }

            // uid are not the same
            return false;
        }

    };

    @Override
    public void onCreate() {
        super.onCreate();
    }

    // disconnect from SIM card service on service termination
    @Override
    public void onDestroy() {
        hideNotifIcon();
        disconnectSEService();
        super.onDestroy();
    }

    // service starting
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return Service.START_NOT_STICKY;
    }

    // new connection to service, connect SIM card service if not connected
    @Override
    public IBinder onBind(Intent intent) {
        // Select the interface to return.  If your service only implements
        // a single interface, you can just return it here without checking
        // the Intent.
        if (IRemoteService.class.getName().equals(intent.getAction())) {
            showProtectedNotifIcon();
            Logd(TAG, "onBind Service");
            connectSEService();
            return mBinder;
        }
        if (IRemoteServiceInternal.class.getName().equals(intent.getAction())) {
            Logd(TAG, "onBind ServiceInternal");
            connectSEService();
            return mBinderInternal;
        }
        return null;
    }

    // connect to SIM card service
    public void connectSEService() {
        // check if not already connected
        if (SIMStorage.seService != null) {
            if (SIMStorage.seService.isConnected())
                return;
        }

        // instantiate SEService object
        try {
            Logd(TAG, "creating SEService object");
            SIMStorage.seService = new SEService(this, this);
        } catch (SecurityException e) {
            Log.e(TAG, "Binding not allowed, uses-permission org.simalliance.openmobileapi.SMARTCARD?");
        } catch (Exception e) {
            Log.e(TAG, "connectSEService exception: " + e.getMessage());
        }
    }

    // disconnect from SIM card service
    public void disconnectSEService() {
        if (SIMStorage.seService != null && SIMStorage.seService.isConnected()) {
            SIMStorage.seService.shutdown();
        }
    }

    // SIM card service connection notification
    public void serviceConnected(SEService service) {
        // Log.i(LOG_TAG, "seviceConnected()");
        Toast.makeText(this, "SIM CARD SERVICE CONNECTED", Toast.LENGTH_SHORT).show();
    }

    void setClientTokens(String id, String tokens) {
        Logd(TAG, "setClientTokens id:" + id);
        Logd(TAG, "setClientTokens tokens:" + tokens);
        RemoteListenerToken rl = null;
        synchronized (RemoteListenerTokenList) {
            for (int i = RemoteListenerTokenList.size() - 1; i >= 0; i--) {
                RemoteListenerToken r = RemoteListenerTokenList.get(i);
                if (r.id.compareTo(id) == 0) {
                    rl = r;
                    RemoteListenerTokenList.remove(i);
                    break;
                }
            }
        }

        if (rl != null) {

            JSONObject jObject = null;
            try {
                if (tokens != null)
                    jObject = new JSONObject(tokens);

                if (jObject != null) {
                    // String access_token  = getFromJS( jObject, "access_token" );
                    // String token_type    = getFromJS( jObject, "token_type" );
                    String refresh_token = getFromJS(jObject, "refresh_token");
                    String expires_in = getFromJS(jObject, "expires_in");
                    String id_token = getFromJS(jObject, "id_token");
                    rl.ocp.m_server_scope = getFromJS(jObject, "scope");
                    boolean user_cancel = false;
                    String userCancel = getFromJS(jObject, "cancel");
                    if (userCancel != null && userCancel.equalsIgnoreCase("true")) {
                        user_cancel = true;
                    }

                    // android.os.Debug.waitForDebugger();

                    if (rl.useTim) {
                        // put id_token and refresh_token in Storage ( SIM or SDCard )
                        secureStorage.save_tokens(rl.ocp, id_token, refresh_token, expires_in);
                        String tim_token = secureStorage.getNewTimToken(rl.ocp);

                        rl.listener.handleTokenResponseWithTim(id_token, tim_token, user_cancel);
                    } else {

                    }

                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (rl != null && rl.listener != null) {
            try {
                if (rl.useTim)
                    rl.listener.handleTokenResponseWithTim(EMPTY, EMPTY, false);
                else
                    rl.listener.handleTokenResponse(EMPTY);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    // get a string from a json object
    String getFromJS(JSONObject jo, String name) {
        if (jo != null) {
            try {
                return jo.getString(name);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    String sortScope(String scope) {
        // sort scope in alphabetical order
        if (scope != null) {
            scope = scope.toLowerCase(Locale.getDefault());
            // offline_access is mandatory
            if (!scope.contains("offline_access")) {
                scope += " offline_access";
            }
            /*
            // and tim too for php oidc
            if ( !scope.contains("tim") ) {
               scope += " tim";
            }
            */
            String scopes[] = scope.split("\\ ");
            if (scopes != null) {
                Arrays.sort(scopes, new Comparator<String>() {
                    @Override
                    public int compare(String s1, String s2) {
                        return s1.compareToIgnoreCase(s2);
                    }
                });
                scope = null;
                // filter null or empty strings
                for (int i = 0; i < scopes.length; i++) {
                    if (scopes[i] != null && scopes[i].length() > 0) {
                        if (scope == null)
                            scope = scopes[i];
                        else
                            scope += (" " + scopes[i]);
                    }
                }
            }
        }
        return scope;
    }

    void toast(final String msg, final int duration) {
        new android.os.Handler(android.os.Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                android.widget.Toast.makeText(theService, msg,
                        duration == 0 ? android.widget.Toast.LENGTH_SHORT : android.widget.Toast.LENGTH_LONG)
                        .show();
            }
        });
    }

    private void showProtectedNotifIcon() {
        showNotification(true);
    }

    private void showNotProtectedNotifIcon() {
        showNotification(false);
    }

    private void showNotification(boolean bProtect) {
        Logd(TAG, "show protected icon " + bProtect);

        // this is it, we'll build the notification!
        // in the addAction method, if you don't want any icon, just set the first param to 0
        Notification mNotification = null;

        if (bProtect) {
            mNotification = new Notification.Builder(this)

                    .setContentTitle("TIM").setContentText("privacy protected").setSmallIcon(R.drawable.masked_on)
                    .setAutoCancel(false).build();
        } else {
            mNotification = new Notification.Builder(this)

                    .setContentTitle("TIM").setContentText("privacy not protected")
                    .setSmallIcon(R.drawable.masked_off).setAutoCancel(false).build();
        }

        // to make it non clearable
        mNotification.flags |= Notification.FLAG_NO_CLEAR;

        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        // If you want to hide the notification after it was selected, do the code below
        // myNotification.flags |= Notification.FLAG_AUTO_CANCEL;

        notificationManager.notify(0, mNotification);
    }

    private void hideNotifIcon() {
        Logd(TAG, "hideNotifIcon");

        if (Context.NOTIFICATION_SERVICE != null) {
            String ns = Context.NOTIFICATION_SERVICE;
            NotificationManager nMgr = (NotificationManager) getApplicationContext().getSystemService(ns);
            nMgr.cancel(0);
        }
    }

    void Logd(String tag, String msg) {
        if (tag != null && msg != null)
            Log.d(tag, msg);
    }

}