1e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal/*
2e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * Copyright (C) 2014 The Android Open Source Project
3e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal *
4e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * Licensed under the Apache License, Version 2.0 (the "License");
5e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * you may not use this file except in compliance with the License.
6e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * You may obtain a copy of the License at
7e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal *
8e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal *      http://www.apache.org/licenses/LICENSE-2.0
9e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal *
10e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * Unless required by applicable law or agreed to in writing, software
11e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * distributed under the License is distributed on an "AS IS" BASIS,
12e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * See the License for the specific language governing permissions and
14e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal * limitations under the License.
15e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal */
16e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
17e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalpackage com.android.launcher3.compat;
18e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
19e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport android.content.Context;
20e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport android.content.pm.PackageInstaller;
21e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport android.content.pm.PackageInstaller.SessionCallback;
22800a4f217a5b0b7817a70a24974fc13d6b7e4591Sunny Goyalimport android.content.pm.PackageInstaller.SessionInfo;
23d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyalimport android.os.Handler;
24e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport android.util.Log;
25e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport android.util.SparseArray;
26e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
27349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyalimport com.android.launcher3.IconCache;
28e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport com.android.launcher3.LauncherAppState;
29e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
30e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyalimport java.util.ArrayList;
319448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyalimport java.util.HashSet;
32e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
33d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyalpublic class PackageInstallerCompatVL extends PackageInstallerCompat implements Runnable {
34e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
35e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private static final String TAG = "PackageInstallerCompatVL";
36e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private static final boolean DEBUG = false;
37e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
38d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal    // All updates to these sets must happen on the {@link #mWorker} thread.
39800a4f217a5b0b7817a70a24974fc13d6b7e4591Sunny Goyal    private final SparseArray<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
40a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal    private final HashSet<String> mPendingBadgeUpdates = new HashSet<String>();
41d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal
42e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private final PackageInstaller mInstaller;
43349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal    private final IconCache mCache;
44d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal    private final Handler mWorker;
45e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
46e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private boolean mResumed;
47e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private boolean mBound;
48e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
49e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    PackageInstallerCompatVL(Context context) {
50e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mInstaller = context.getPackageManager().getPackageInstaller();
515610fe72e01ac752153f58a3c9e615fcb997df35Sunny Goyal        LauncherAppState.setApplicationContext(context.getApplicationContext());
52349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        mCache = LauncherAppState.getInstance().getIconCache();
53d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        mWorker = new Handler();
54e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
55e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mResumed = false;
56e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mBound = false;
57e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
58d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        mInstaller.registerSessionCallback(mCallback, mWorker);
59393afd34566ba51f817c45a55305a9ac150d7f5bDharmesh Mokani
60e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        // On start, send updates for all active sessions
61d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        mWorker.post(new Runnable() {
62d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal
63d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal            @Override
64d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal            public void run() {
65d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal                for (SessionInfo info : mInstaller.getAllSessions()) {
66d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal                    mPendingReplays.append(info.getSessionId(), info);
67d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal                }
68d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal            }
69d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        });
70e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
71e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
72e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    @Override
739448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyal    public HashSet<String> updateAndGetActiveSessionCache() {
749448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyal        HashSet<String> activePackages = new HashSet<String>();
75349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        UserHandleCompat user = UserHandleCompat.myUserHandle();
76349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        for (SessionInfo info : mInstaller.getAllSessions()) {
77349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal            addSessionInfoToCahce(info, user);
789448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyal            if (info.getAppPackageName() != null) {
799448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyal                activePackages.add(info.getAppPackageName());
809448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyal            }
81349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        }
829448536b113afa6ceefce26604a1b44618d1d4f2Sunny Goyal        return activePackages;
83349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal    }
84349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal
85349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal    private void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) {
86349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        String packageName = info.getAppPackageName();
87349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        if (packageName != null) {
88349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal            mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
89349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal                    info.getAppLabel());
90349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        }
91349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal    }
92349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal
93349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal    @Override
94e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    public void onStop() {
953d0854e4b8c0a6daaa80ac14c6bc7572a4e90916Dharmesh Mokani        mInstaller.unregisterSessionCallback(mCallback);
96e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
97e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
98e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    @Override
99e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    public void onFinishBind() {
100e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mBound = true;
101d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        mWorker.post(this);
102e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
103e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
104e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    @Override
105e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    public void onPause() {
106e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mResumed = false;
107e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
108e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
109e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    @Override
110e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    public void onResume() {
111e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mResumed = true;
112d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        mWorker.post(this);
113e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
114e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
115e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    @Override
116e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    public void recordPackageUpdate(String packageName, int state, int progress) {
117e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        // No op
118e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
119e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
120d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal    @Override
121d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal    public void run() {
122d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        // Called on mWorker thread.
123d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal        replayUpdates(null);
124d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal    }
125d90e938db0523c13f0301ea8cbb2cef4cf044efcSunny Goyal
126e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private void replayUpdates(PackageInstallInfo newInfo) {
127e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        if (DEBUG) Log.d(TAG, "updates resumed");
128e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        if (!mResumed || !mBound) {
129e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            // Not yet ready
130e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            return;
131e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
132e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        if ((mPendingReplays.size() == 0) && (newInfo == null)) {
133e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            // Nothing to update
134e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            return;
135e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
136e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
137e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
138e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        if (app == null) {
139e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            // Try again later
140e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            if (DEBUG) Log.d(TAG, "app is null, delaying send");
141e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            return;
142e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
143e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
14465c6011d0954223c14a0e6127cf307c18f7c2c0dSameer Padala        ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
145349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) {
146e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            updates.add(newInfo);
147e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
148349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal        for (int i = mPendingReplays.size() - 1; i >= 0; i--) {
149800a4f217a5b0b7817a70a24974fc13d6b7e4591Sunny Goyal            SessionInfo session = mPendingReplays.valueAt(i);
150e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            if (session.getAppPackageName() != null) {
151e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                updates.add(new PackageInstallInfo(session.getAppPackageName(),
152349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal                        STATUS_INSTALLING,
153e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                        (int) (session.getProgress() * 100)));
154e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            }
155e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
156e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        mPendingReplays.clear();
157e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        if (!updates.isEmpty()) {
158e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            app.setPackageState(updates);
159e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
160a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal
161a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal        if (!mPendingBadgeUpdates.isEmpty()) {
162a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            for (String pkg : mPendingBadgeUpdates) {
163a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                app.updatePackageBadge(pkg);
164a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            }
165a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            mPendingBadgeUpdates.clear();
166a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal        }
167e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    }
168e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
169e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    private final SessionCallback mCallback = new SessionCallback() {
170e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
171e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        @Override
172e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        public void onCreated(int sessionId) {
173a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            pushSessionBadgeToLauncher(sessionId);
174e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
175e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
176e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        @Override
177e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        public void onFinished(int sessionId, boolean success) {
178e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            mPendingReplays.remove(sessionId);
179800a4f217a5b0b7817a70a24974fc13d6b7e4591Sunny Goyal            SessionInfo session = mInstaller.getSessionInfo(sessionId);
180e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            if ((session != null) && (session.getAppPackageName() != null)) {
181a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                mPendingBadgeUpdates.remove(session.getAppPackageName());
182e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                // Replay all updates with a one time update for this installed package. No
183e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                // need to store this record for future updates, as the app list will get
184e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                // refreshed on resume.
185e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                replayUpdates(new PackageInstallInfo(session.getAppPackageName(),
186349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal                        success ? STATUS_INSTALLED : STATUS_FAILED, 0));
187e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            }
188e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
189e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
190e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        @Override
191e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        public void onProgressChanged(int sessionId, float progress) {
192800a4f217a5b0b7817a70a24974fc13d6b7e4591Sunny Goyal            SessionInfo session = mInstaller.getSessionInfo(sessionId);
193e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            if (session != null) {
194e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                mPendingReplays.put(sessionId, session);
195e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal                replayUpdates(null);
196e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal            }
197e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        }
198e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
199e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        @Override
200f9847b9bd3e5286fe10667ec12dab5b4c43355faDharmesh Mokani        public void onActiveChanged(int sessionId, boolean active) { }
201e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal
202e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal        @Override
203a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal        public void onBadgingChanged(int sessionId) {
204a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            pushSessionBadgeToLauncher(sessionId);
205a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal        }
206a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal
207a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal        private void pushSessionBadgeToLauncher(int sessionId) {
208a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            SessionInfo session = mInstaller.getSessionInfo(sessionId);
209a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            if (session != null) {
210a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                addSessionInfoToCahce(session, UserHandleCompat.myUserHandle());
211a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                if (session.getAppPackageName() != null) {
212a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                    mPendingBadgeUpdates.add(session.getAppPackageName());
213a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                }
214a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                mPendingReplays.put(sessionId, session);
215a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal                replayUpdates(null);
216a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal            }
217a22666f68151269853e7ab6ab696ee6361bac793Sunny Goyal        }
218e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal    };
219e755d469d40b95e763a9dcb67d0e4f511d1948ddSunny Goyal}
220